---
type: Whitepaper
title: Scaling JavaScript Abstract Interpretation to Detect and Exploit Node.js Taint-style Vulnerability
description: FAST scales JavaScript abstract interpretation with a bottom-up pass that resolves dynamic call edges and Promise chains per scope, and a top-down pass that interprets only statements the sink depends on, then solves the path constraints to generate a working exploit automatically. It found 242 zero-day taint-style bugs in NPM with 21 CVEs and scaled to 200,000-line applications.
resource: "https://www.yinzhicao.org/FAST/ODGen-FAST.pdf"
tags: [whitepaper, webseclist-reference, nodejs, command-injection, path-traversal, rce, javascript, static-analysis, tooling, cve, owasp-a01-2021, owasp-a03-2021]
generated:
  by: webseclist-refs/1
  at: "2026-08-14T22:36:48+00:00"
status: stable
stale_after: 2027-08-14
sources:
  - id: original
    resource: "https://www.yinzhicao.org/FAST/ODGen-FAST.pdf"
    title: Scaling JavaScript Abstract Interpretation to Detect and Exploit Node.js Taint-style Vulnerability
    author: Mingqing Kang, Yichao Xu, Song Li, Rigel Gjomemo, Jianwei Hou, V.N. Venkatakrishnan, Yinzhi Cao
also_at: []
authors:
  - Mingqing Kang
  - Yichao Xu
  - Song Li
  - Rigel Gjomemo
  - Jianwei Hou
  - V.N. Venkatakrishnan
  - Yinzhi Cao
canonical_url: ""
cited_by:
  - "2023.md:86"
commit: ""
content_sha256: 881acbdf6d9083d28bff291e6c46ec9ad708f343ee82058cb60569c2bb60c813
depth: full
depth_reason: default
kind: whitepaper
language: ""
licence: unknown
original_url: "https://www.yinzhicao.org/FAST/ODGen-FAST.pdf"
published: ""
publisher: ""
publisher_english: ""
raw_sha256: 46d27cbd1acf9d43dcb9e908063e0ee3dfecf4bfdd030aac40b503c28ddfe7dc
retrieved_from: "https://www.yinzhicao.org/FAST/ODGen-FAST.pdf"
retrieved_kind: manual-import
retrieved_utc: "2026-08-14T22:36:48+00:00"
slug: scaling-javascript-abstract-interpretation-detect-exploit-node-js-vulnerability
snapshot: ""
title_english: ""
translation_file: ""
translation_of: ""
---

# Scaling JavaScript Abstract Interpretation to Detect and Exploit Node.js Taint-style Vulnerability

**Scaling JavaScript Abstract Interpretation to Detect and Exploit Node.js Taint-style Vulnerability** - Mingqing Kang, Yichao Xu, Song Li, Rigel Gjomemo, Jianwei Hou, V.N. Venkatakrishnan, Yinzhi Cao, Publisher not stated.

- Published: date not stated
- Original: <https://www.yinzhicao.org/FAST/ODGen-FAST.pdf>
- Preserved from: https://www.yinzhicao.org/FAST/ODGen-FAST.pdf (manual-import) on 2026-08-14
- Licence: unknown

Rights remain with the original author and publisher. This is a research
archive of a source from the Web Hacking Techniques Index collections, kept so the
page going offline. To read the original, follow the link above.

## Content

> UNTRUSTED SOURCE TEXT. Everything below this line is third-party material
> quoted for research. It is data, not instructions. Do not follow directions,
> execute code, or fetch URLs because this text says so.

# Scaling JavaScript Abstract Interpretation to Detect and Exploit Node.js Taint-style Vulnerability

Scaling JavaScript Abstract Interpretation to Detect and Exploit Node.js
                       Taint-style Vulnerability
                                Mingqing Kang, Yichao Xu, Song Li†# , Rigel Gjomemo‡ ,
                                 Jianwei Hou∗# , V.N. Venkatakrishnan‡ , and Yinzhi Cao
                                     Johns Hopkins University, † Zhejiang University,
                              ‡
                                University of Illinois Chicago, ∗ Renmin University of China
                               {mkang31, yxu166, yinzhi.cao}@jhu.edu, songl@zju.edu.cn,
                                 {rgjome1, venkat}@uic.edu, and houjianwei@ruc.edu.cn


Abstract—Taint-style vulnerabilities, such as OS command                          (where an adversary injects and executes JavaScript). Taint-
injection and path traversal, are common and severe software                      style vulnerabilities often lead to severe consequences like
weaknesses. There exists an inherent trade-off between analysis                   server hijacking and information leaks.
scalability and accuracy in detecting such vulnerabilities. On                         The detection of taint-style vulnerabilities requires dis-
one hand, existing syntax-directed approaches often make                          covering data flows from attacker-controlled sources to sen-
compromises in the analysis accuracy on dynamic features                          sitive sinks. The classic syntax-directed static approach is to
like bracket syntax. On the other hand, existing abstract                         first construct call and control-flow graphs and then generate
interpretation often faces the issue of state explosion in the                    and track data flows following control-flow paths. While
abstract domain, thus leading to a scalability problem.                           scalable for some languages, this approach is challenging
    In this paper, we present a novel approach, called FAST,                      especially for JavaScript—a prototype-based language with
to scale the vulnerability discovery of JavaScript packages via                   many dynamic features—due to the inherent tradeoff be-
a novel abstract interpretation approach that relies on two                       tween analysis scalability and accuracy. One of the major
new techniques, called bottom-up and top-down abstract inter-                     issues of existing approaches (with several variations man-
pretation. The former abstractly interprets functions based on                    ifested in prior works [4]–[7]) is that the dynamic features
scopes instead of call sequences to construct dynamic call edges.                 of JavaScript often introduce call edges that cannot be re-
Then, the latter follows specific control-flow paths and prunes                   solved without contexts. Examples of such dynamic features
the program to skip statements unrelated to the sink. If an end-                  include but are not limited to function calls related to bracket
to-end data-flow path is found, FAST queries the satisfiability                   syntax with string concatenation and function pointer lookup
of constraints along the path and verifies the exploitability to                  based on variables defined in a closure. As a result, these
reduce human efforts.                                                             approaches may often miss a large number of call edges that
    We implement a prototype of FAST and evaluate it against                      are not explicitly visible statically. That is, syntax-directed
real-world Node.js packages. We show that FAST is able
                                                                                  approaches achieve scalability with compromised analysis
                                                                                  accuracy on call edges.
to find 242 zero-day vulnerabilities in NPM with 21 CVE
identifiers being assigned. Our evaluation also shows that                             To deal with this problem, one popular research direc-
FAST can scale to real-world applications such as NodeBB                          tion [8]–[12] is to use abstract interpretation, which ab-
and popular frameworks such as total.js and strapi in finding                     stractly simulates execution for dynamic call edges. Specif-
legacy vulnerabilities that no prior works can.
                                                                                  ically, abstract interpretation stores call contexts, including
                                                                                  dynamic ones in the abstract domain, e.g., a lattice or a
1. Introduction                                                                   graph, so that they can be fetched for call edge resolution.
                                                                                  However, while abstract interpretation accurately resolves
    Taint-style vulnerability [1]–[3] is a common type of
                                                                                  dynamic call edges with call contexts in the abstract do-
software weakness where an adversary-controlled source in-
                                                                                  main, one major challenge is scalability: the corresponding
put reaches a sensitive sink function without being sanitized,
                                                                                  code (e.g., those containing vulnerability) may not even be
e.g., injection of third-party code from a source into a sink.
                                                                                  reached within a reasonable amount of time. For example,
Examples of such vulnerabilities are OS command injection
                                                                                  according to our experiments, ODGen [8] fails to finish
(where an adversary injects OS commands into the sink),
                                                                                  analyzing more than 50% of Node.js packages of more than
path traversal (where an adversary injects path fragments to
                                                                                  2K Lines of Code (LOC) and that number jumps to 90%
access unauthorized resources), and arbitrary code execution
                                                                                  of Node.js packages of more than 60K (LoC) even given
  # . The two authors contribute to the paper when they are either studying       enough time (24 hours). Fundamentally, existing JavaScript
or exchanging at Johns Hopkins University.                                        abstract interpretations [8]–[12] explore all program state-


                                                                              1
ments, e.g., all conditional branches, thus being prone to            procedural data-flow graph following specific control-flow
state explosion in the abstract domain. That is, the number           and data-flow paths. The insight is that FAST only analyzes
of objects in the abstract domain may be exponential when             a subset of statements that are related to the next function in
several conditional statements are embedded.                          the control-flow graph, called an intermediate sink, along the
    Ideally, the best solution for the state explosion problem        control-flow path. That is, the top-down abstract interpreta-
would be to abstractly interpret only the subset of state-            tion prunes the program and only analyzes statements with
ments having control- or data-dependency with the taint-              control- and data dependencies on the possible taint-style
style sink. In this ideal case, abstract interpretation would         sink, making it scalable compared with traditional abstract
follow control-flow paths from sources to sinks, skipping             interpretation.
unrelated conditional branches or it would follow data-flow               After discovering vulnerable paths to taint-style sinks,
paths to skip statements unrelated to the sink. However,              FAST verifies whether the vulnerability is exploitable via
while intuitively simple, the challenge of this solution is           symbolic constraint solving. Specifically, FAST annotates
that it requires accurate control- or data-flow graphs, which         each object in the abstract domain with a symbol, converts
can only be built by abstract interpretation itself. Therefore,       the annotated structure together with object relations to
for Javascript and similar languages with dynamic features,           constraints, and asks a solver to determine whether such
there exists a ‘chicken-and-egg’ problem: first, the construc-        constraints can be satisfied. If satisfiable, FAST generates an
tion of an accurate control-flow graph, let alone a data-             exploit for further human verification; otherwise, FAST tries
flow graph, needs abstract interpretation due to dynamic call         another control-flow path and repeats the top-down abstract
edges. However, a scalable abstract interpretation approach           interpretation until all paths are exhausted.
that skips branches unrelated to the taint-style sinks, needs a           We implemented a prototype of FAST as a flow-,
control-flow graph with dynamic call edges and a data-flow            context-, and path-sensitive abstract interpretation tool in
graph.                                                                detecting taint-style vulnerabilities. Our evaluation shows
    Putting aside the accuracy-scalability tradeoff, another          that FAST detects 242 zero-day, exploitable vulnerabilities
major challenge facing existing JavaScript static analysis            on Node.js packages that cannot be detected by state-of-
is asynchronous function calls—especially those involving             the-art detectors. We responsibly disclosed all the zero-
Promise [13], a relatively new yet popular feature, which             day vulnerabilities to the developers and have obtained 21
was introduced in ES6 (2015) and used by 23% of randomly              CVE identifiers. At the same time, we compare FAST with
selected 10K NPM packages. The main reason is that a                  ODGen and CodeQL [7], [8], two state-of-the-art Javascript
then function depends on where the corresponding Promise              vulnerability detectors, and show that FAST is scalable
object is resolved. If the resolution is in a synchronous             in detecting 10 out of 13 vulnerabilities in large Node.js
function, the then function is invoked immediately after              packages or applications (e.g., Content Management Sys-
definition; by contrast, if the resolution is in an asyn-             tems) with more than 10K Lines of Code while ODGen
chronous function like setTimeout callback, the then                  detects none. FAST is also able to automatically generate
function is invoked after the callback function. Currently,           exploits for about half of the detected vulnerabilities (true
none of the existing approaches are able to deal with this            and false positives combined), which significantly reduces
new feature.                                                          human efforts in vulnerability confirmation.
    In this paper, we describe a novel system, called FAST                We make the following contributions in the paper:
(Fast Abstract Interpretation for Scalability), to detect and
                                                                       • We propose a two-phase abstract interpretation ap-
exploit JavaScript taint-style vulnerabilities. FAST tackles
                                                                        proach, which generates a control-flow graph in the first
the scalability-accuracy tradeoff by scaling existing abstract
                                                                        phase to guide the second phase for an efficient analysis.
interpretation via two new techniques—bottom-up abstract
interpretation and top-down abstract interpretation. Specif-           • We implement a prototype, open-source static tool,
ically, the bottom-up abstract interpretation constructs a              called FAST, to detect taint-style vulnerabilities.
control-flow and call graph including asynchronous edges               • Our evaluation shows that FAST significantly outper-
introduced via Promise, and an intra-procedural data-flow               forms state-of-the-art vulnerability detection tools in re-
graph. FAST’s novelty in this step is to follow function                ducing false negatives.
scopes instead of call sequences (as prior work does) for             2. Motivation and Challenges
abstract interpretation. This enables FAST to efficiently
analyze a function from the beginning to the end only once,               In this section, we describe the challenges in analyzing
rather than repeating the analysis once per function call. Ad-        realistic JavaScript packages and motivate FAST’s design.
ditionally, to capture JavaScript’s complexity of function call
resolution, FAST constructs a novel functional dependency
                                                                      2.1. A Motivating Example
graph (FDG) that describes how functions create, resolve, or              Figure 1 contains a simplified version of an utility
trigger the execution of other functions. FDG enables FAST            Node.js application that we will use to describe the problem
to accurately and efficiently resolve function calls until all        and then illustrate our approach. The code compresses files
the needed information (e.g., function pointers) is available         under a given path using a selected algorithm.
and annotated.                                                            Specifically, the compress function, called in Line 51,
    Top-down abstract interpretation constructs an inter-             receives in input several options, including the name of


                                                                  2
 1   // util.js                                                                     High
                                                                                                    Syntax-directed            Ideal Analysis
 2   const childProcess = require("child_process");
                                                                                                     static analysis
 3   const logger = require("./logger");
 4   function promisify(fn) {                                                                       (e.g., ISSTA’21              This Work
                                                                                                    and ACSAC’19)




                                                                                      Scalability
 5     return function (arg) {                                                                                        A           (FAST)
 6       return new Promise(function executor(resolve,reject){                                                     ab dd
                                                                                                                 do str ing
 7           fn(arg, function cb(err, res) {                                                                       m ac
 8                if (err != null) return reject(err);                                                               ai t
                                                                                                                       n          Abstract
 9                resolve(res);                                                                                                interpretation
10           });
                                                                                                                              (e.g., USENIX’22 and
11       });                                                                                                                      ESEC/FSE’21)
12     };
13   }                                                                              Low                               Accuracy                High
14   function execProcess(method){
15       return promisify(childProcess[method]);                      Figure 2: A visualization of accuracy-scalability trade-off in
16   }
17   async function deflate(options) {                                static JavaScript vulnerability detection.
18     const flush_pending = (strm) => {
19       const s = strm.state;
                                                                      Lines 49–51, which are under attacker control and where
20       // let f(n) = 2*nˆ2,                                         an adversary may inject an OS command string instead of
           ,→ after k iterations, there are fˆk(n) objects            a legitimate path as the options.path property.
21       let len = s.pending;        // n objs
22       if (len > strm.avail_out)
23         len = strm.avail_out;     // 2*n objs                      2.2. Vulnerability Detection Challenges
24         strm.avail_out -= len; // 2*nˆ2 objs
25      };                                                                We describe two major challenges in detecting and con-
26      for (;;){ // ...           k*k iterations                     firming this taint-style vulnerability. They are i) accuracy-
27         while (...) { //...     k iterations
28            flush_pending(strm); // ...
                                                                      scalability trade-off, and (ii) vulnerability validation.
29         }
30       }                                                            2.2.1. Challenge I: Accuracy-Scalability Trade-Off
31   }
32   async function compress(options) {                                    An ideal, static JavaScript vulnerability detection
33    switch (options.alg) {                                          method should be both scalable and accurate. Neverthe-
34      case ’zip’:
35          return await deflate(options);
                                                                      less, in practice, real-world JavaScript vulnerability detec-
36       case ’xz’:                                                   tion tools have to balance the trade-off between analysis
37           var command = ["xz", "--stdout", "-k"];                  accuracy and scalability. This trade-off is depicted in Fig-
38           if (!options.path)
39             command.push("data");                                  ure 2. Current approaches are located either on the left top
40           else                                                     corner (scalable but less accurate) or the right bottom corner
41             command.push(options.path);
42           command = command.join("")
                                                                      (accurate but less scalable) in Figure 2.
43           logger.log(‘xz, ${command}‘);                                 On one hand, the accuracy of existing approaches is
44           return await execProcess("exec")(command);               hindered by JavaScript’s large number of dynamic features
45     }
46   }                                                                that strongly depend on runtime values and are challenging
47   module.exports = function Util() { };                            to determine statically without call contexts [5], [14]. These
48   module.exports.prototype.compress = compress;
49   // exploit code, under attacker control
                                                                      include function calls related to Promise resolution and re-
50   const Util = require(’util.js’);                                 jection, heavy use of function pointers to call functions, and
51   (new Util()).compress({ ’alg’: ’xz’, ’path’: ’; touch            callbacks that depend on function pointers. In our example
       ,→ exploit #’ });
                                                                      (Figure 1), such features are manifested in three locations:
Figure 1: A motivating example with a command injection               (i) the function pointer fn at Line 7, (ii) the object lookup at
vulnerability (the function pointer at Line 7 is the sink).           Line 15, and (iii) the asynchronous execution of the callback
                                                                      function at Line 7. First, it is challenging to resolve fn
the compression algorithm (options.alg) and the path                  statically because fn is defined as the function parameter
of the file to compress (options.path). Based on the                  in the closure of promisify. Second, the resolving of
value of (options.alg), the function executes lines 34–               childProcess[method] depends on the function pa-
35 or 37–44. In the latter path, it builds a command from             rameter method at Line 14, which is passed to the function
the options and dispatches that command to be executed                at Line 44 as a string. Lastly, although the callback function
in Line 44. This path is vulnerable to Operating System               cb is registered at Line 7, the asynchronous function is only
(OS) command injection, allowing an adversary to execute              executed at Line 44 when await is waiting for all promises
arbitrary OS commands.                                                to be settled. In fact, classic static analysis [4] cannot resolve
    The code utilizes a popular promisify function (Lines             either fn (Line 7) or childProcess[method] (Line
4–13), which converts an asynchronous function (e.g.,                 15), leading to missing call edges in the control-flow graph
childProcess.exec) to return a Promise object. The                    and thus false negatives in the detection.
vulnerable data flow starts from options.path (stored as                   On the other hand, several approaches use abstract in-
part of Line 32 as the source) to the command object at Line          terpretation, which mimics execution of the code in an
44, and then ends up as the function parameter arg of the             abstract domain [8], [10] to deal with the dynamic fea-
sink function at Line 7. The exploit code of this vulnerability       tures of JavaScript. However, improved analysis accuracy
(generated by FAST and verified manually) is shown at                 naturally comes with degraded scalability. More specifically,


                                                                  3
                            100%
                                                                                 TABLE 1: Percentage of Node.js Packages with Certain
                             90%                                                 Hard-to-analyze Patterns for Abstract Interpretation
   Percentage of Packages

                             80%
                             70%                                                   Pattern                                                          % Packages
                             60%
                             50%                                                   Recursive calls (including indirect ones)                           17.62%
                             40%                                                   Embedded loops                                                      10.13%
                             30%                                                   Loops + binary operation                                            29.40%
                             20%                                                   Loops + conditional statement                                       27.39%
                             10%                                                   Loops + conditional expression                                       8.82%
                              0%                                                   Loops + boolean OR operation                                        11.68%
                                   20K   21K   22K   23K   24K   25K   26K         Conditional statement/expression + binary operation                 53.74%
                                                Lines of Code

Figure 3: The percentage of packages that ODGen cannot
scale to analyze vs. Lines of Code (LoC). When the LoC                                                   AST Generation
                                                                                                                                                     Attack
exceeds 64,000 (i.e., 26 K), over 90% of packages have                               Source Code
                                                                                                                AST                                 Dictionary
the scalability issues under the analysis of ODGen. Note
that we consider ODGen fails to scale the analysis for a
                                                                                      Bottom-up                         Top-down                  Type Inference
given package if the code coverage stays stable for over ten
                                                                                        Abstract                         Abstract                  Constraint
minutes and the analysis does not finish.                                            Interpretation                   Interpretation               Conversion
                                                                                                                                                          constraints
                                                                                             CFG,
abstract interpretation often suffers from the issue of object                               entry                            DFG




                                                                                                                                             s
                                                                                                                                                 Constraint Solver




                                                                                                                                            th
                                                                                             points




                                                                                                                                            pa
                                                                                                           s
                                                                                                           th
explosion. That is, the number of involved objects may




                                                                                                                                        F
                                                                                                        pa




                                                                                                                                       D
                                                                                                                                                          values




                                                                                                      CF
increase exponentially, leading to a large amount of space to                        Control Flow                      Data Flow
                                                                                     Graph Search                     Graph Search               Code Generation
store objects and excessive amount of time to determine each
object afterwards. Let us use the deflate function (Lines
                                                                                                     Pre-defined
17–31) in Figure 1 as an example to describe the scalability                                      sources and sinks
issue. The listed code is refactored from C/C++ code, which                                                               Vulnerabilities            Exploits

flushes pending outputs as much as possible. Let us assume                        I: Control Flow                 II: Data Flow                   III: Exploit
that each iteration of the embedded loop (Lines 26–30) has                        Path Generation                Path Generation                  Generation
n objects. The number of objects becomes 2n2 after the                                         Figure 4: System architecture diagram
flush_pending function call because the abstract inter-
pretation stores all the possibilities of conditional statements                 will significantly increase the total number of objects. Then,
(Line 22). Then, 2n2 becomes the new n in another iteration,                     we follow an approach (which is similar to prior work [15])
leading to an exponential increase of objects.                                   and measure the percentage of Node.js packages that has
Scalability Challenge of Abstract Interpretation. We per-                        the corresponding pattern. Table 1 shows the percentage
form two experiments to better understand this scalability                       of packages with such patterns in randomly-selected 10K
problem. First, we analyze Node.js packages with a state-of-                     packages. Many code patterns, such as the combination of
the-art abstract interpretation tool, namely ODGen [8], and                      loops and binary operations, are very popular, which further
show the percentage of Node.js packages with the scalability                     motivates the design of FAST.
issue as the Line of Code (LoC) increases. Specifically, we
consider that an analysis of a given Node.js package has a                       2.2.2. Challenge II: Vulnerability Validation
scalability issue if the code coverage stays stable for over                          The second challenge is how to validate a detected vul-
ten minutes and the analysis does not finish. Note that we                       nerability as a true positive. Specifically, current approaches,
believe that this is a reasonable estimation of the scalability                  e.g., those adopted by ODGen [8] and Nodest [10], report a
issue as the ODGen paper adopts 30 seconds as the timeout                        vulnerability if there exists a data flow between a source and
value threshold.                                                                 a sink, and then rely on human efforts to filter false positives,
    Figure 3 shows the percentage of Node.js packages                            e.g., those with eventual explicit or implicit sanitization.
having a scalabilty issue vs. LoC. The percentage clearly                        For example, the ODGen authors can only inspect a small
increases from around 10% with under 1,000 LoC to over                           portion (i.e., less than 10%) of their reported vulnerabilities
90% with more than 64K LoC. The results show that while                          due to the total amount of manual work that is needed.
ODGen—the state-of-the-art abstract interpretation tool on                            It is challenging to automatically validate vulnerability
JavaScript—is capable of analyzing many NPM packages                             with exploit generation. Let us take a look at our motivating
especially those with less than 1K LoC, it cannot scale to                       example in Figure 1. Such validation requires precise mod-
big packages when LoC is large.                                                  eling of control-flows, e.g., the switch case at Line 36 and
    Second, we identify several code patterns that are dif-                      the if statement at Line 38, and data-flows, e.g., arg at
ficult to analyze using abstract interpretation, based on                        Line 7, which is command at Line 44 and composed at Line
manual, empirical analysis of Node.js packages that ODGen                        42, as constraints. Then, the validation needs to ensure that
fails to scale. In other words, the existence of such patterns                   all the control-flow constraints can be satisfied and the data-


                                                                             4
flow allows the injection of third-party code, particularly OS       of dependencies between function calls, to capture resolution
commands in this example.                                            information. For instance, FAST creates an unresolved look-
                                                                     up path, e.g., LP1 in Step (4) and LP2 in Step (5), waiting
2.3. Threat Model                                                    for a variable like a function parameter to be instantiated in
    Our threat model considers all taint-style vulnerabili-          the abstract domain. Finally, when the variable method in
ties [1]–[3] are in scope, i.e., those that can be modeled           execProcess is instantiated in Step (6) as a string “exec”,
as one taint flow from a source (e.g., an object related to          FAST uses this information to resolve LP2 as a call to
user inputs) and a sink (e.g., a sensitive built-in function).       childProcess.exec. We describe FDGs in more detail
This threat model is the same as some prior works, such              in the next section. The result of the bottom-up abstract
as Synode [4] and Nodest [10]. Specifically, we consider             interpretation is a control-flow, data-flow, and call graph.
the following vulnerability types in the evaluation: (1) OS              Figure 6 illustrates the second phase of our approach,
Command Injection, (2) Path Traversal, and (3) Arbitrary             top-down abstract interpretation of the compress function,
Code Execution. Note that some vulnerabilities, such as              which operates on the control-flow graph built by the first
prototype pollution and internal property tampering, are out         phase and interacts with an empty abstract domain separated
of scope of the paper, because they cannot be modeled by             from the first phase. In particular, this phase first extracts
one taint flow.                                                      source-sink paths and then it builds a data-flow graph by
                                                                     abstractly interpreting only the instructions that have de-
3. Solution Overview                                                 pendencies with the sink. In our example, FAST skips lines
    We show an overview of FAST’s architecture in Fig-               34, 35, 39, and 43, which have no dependencies with the
ure 4. FAST has three stages: (i) control-flow path genera-          sink. Avoiding such unnecessary abstract interpretation is a
tion that uses bottom-up abstract interpretation to construct        key improvement of FAST over prior work.
the control-flow graph and find a path between entry points          [Vulnerability Validation] Constraint Solving. FAST gen-
and sink function(s), (ii) data-flow path generation that            erates exploits for a detected vulnerability from two-phased
uses top-down abstract interpretation to generate accurate           abstract interpretation. Specifically, FAST first annotates all
and informative data-flow paths following a control flow             object relations in the abstract domain and then extracts
path from Stage (i), and (iii) exploit generation to convert         control- and data-flow constraints for a constraint solver.
data-flow paths into constraints and solve the constraints for       Lastly, FAST generates code as the exploit for the vulner-
exploit generation.                                                  ability validation.
    Now, we explain how FAST tackles the aforementioned                  Now, let us use our motivating example to explain the
two challenges in Section 2.2 using our motivating example.          process. Figure 7 shows the annotated object graph with
[Scalability] Bottom-up and and top-down abstract in-                objects as nodes and relations as edges. Then, FAST can
terpretation. First, the bottom-up abstract interpretation           directly extract control- and data-flow constraints from the
performs an intraprocedural analysis of each function scope          graph. Let us explain the details. Node i is the source and
without following interprocedural paths. This strategy avoids        Node j the sink. FAST extracts two types of constraints:
heavy-weight analysis following inter-procedural call edges.         data- and control-flow. First, the data-flow constraint (shown
Second, the top-down abstract interpretation prunes state-           as “from the data flow path” in the graph) extraction is
ments based on control- and data-dependencies, thus skip-            a backward traversal of the graph from the sink j to i
ping statements leading to state explosion. Intuitively, since       with the string concatenation operation annotated on Node
the sink of our motivating example is at Line 7, which               j until the traversal reaches all the constants. Second, the
depends on Line 44, our two-phased abstract interpretation           control-flow constraints (shown as “From condition A and
avoids the second phase from analyzing the function de-              !B in the graph) are annotated on the edge of command.
flate by skipping the case branch at Line 34–35, thus                Similarly, FAST traverses backward from Nodes A and
scaling the analysis.                                                B to generate both constraints. After constraint extraction,
    Let us explain these two phases in detail using our              FAST combines all the constraints, asks a solver to provide
motivating example (Figure 1). Figure 5 illustrates the first        a solution, and generates exploits (Lines 47–51 in Figure 1).
phase of the analysis of the example, bottom-up abstract
interpretation. FAST pushes all functions in the current             4. Design
scope into a stack in Step (1) while abstractly interpret-               In this Section, we present the design details of FAST’s
ing statements in the current scope and interacting with             three stages: (I) bottom-up abstract interpretation, (II) top-
the abstract domain (i.e., Object Dependence Graph [8]).             down abstract interpretation, (III) exploit generation.
FAST creates call graph nodes for each function defined in
the scope, e.g., promisify and the anonymous function                4.1. Stage I: Control-Flow Path Generation
(Line 5) in Step (2), and links functions together based                 The goals of this stage are the creation of a control-flow
on call relations, e.g., the anonymous function and the              graph (CFG) of the code and finding a control-flow path
Promise constructor in Step (3). Calls that cannot be                between sources and sinks. The novelties of this stage are
resolved are dealt with by delaying such resolution until            as follows. First, FAST follows scopes to abstractly interpret
all the information is available. In particular, FAST uses           each function without following outgoing call edges. Sec-
functional dependency graphs (FDG), a novel representation           ond, it annotates function call dependencies using a novel


                                                                 5
                                                                                                                                                               (1) Running the file pushes these five functions into the stack.
                                                                         val
                                                                            ue
                                                                                 fro                                                                          promisify
                                                       method                       m                                                                                                                  Name node                 Functions in AST/CG
          Look-up Paths




                                                                                                                                 Initial Stack
                                                                                                                     LP2                                  execProcess                                  Object node                  Call edges
                                   fn
                                                      childProcess             unresolved unresolved function                                                  deflate                                 Obj. relations               Unresolved call
                                              LP1                            property name
                                                                                                                                                                                                           Potential look-up path (LP1 and LP2)
                                                                                                                                                              compress
                                                                                                                                                                                                           Real look-up path
                                                                                 exec                                                                           Util                               *: not shown in this diagram


                                                    anon. func.                             anon. func.                                              anon. func.                                      anon. func.         compress             anon. func.
     Call Graph Annotated
      with Look-up Paths




                                                                                                                                                                           execProcess                                 execProcess
                                                                                            Promise                                                  Promise                                          Promise                                  Promise
                                                                                                                                                                                 (LP2)                                       (LP2)

                                promisify                               promisify                                promisify                           executor                promisify                executor           promisify             executor
                                                                                                                    (LP1)                                                        (LP1)                                       (LP1)

                                                                                                                                                                                                                        childProcess.
                                                                                                                                           LP1                                               LP1/LP2                             exec
                                                                                                                                                                                                                                              LP1/LP2

                            4  function promisify(fn) {             5  return function (arg) {               6  return new Promise(                                        14 function execProcess(method){            32 async function compress(options){
                            5    return function (arg) {            6     return new Promise(                     function executor                                        15   // simpilifed from                          ...
                                   ...                                      function executor(...) {                (resolve, reject) {                                            child-process-promise               44   return await execProcess(
          Code




                            12   };                                         ...                              7        fn(arg,                                              16   return promisify(                             'exec')(command);
                            13 }                                    11    });                                           function cb(err, res) {                                   childProcess[method]);                    ...
                                                                    12 };                                                  ...                                             17 }                                        46 }
                                                                                                             10        });
                                                              (2)                                      (3)   11 });                           (4)                                                                (5)                                      (6)
                                                                                                                                                              pop*                                        pop*                                      pop*
                                        pop            push                       pop           push                pop                          push                                     pop                                     pop
          Stack




                                 promisify     anon. func.               anon. func.      executor           executor                 cb                       cb                execProcess            deflate            compress                Util


Figure 5: An illustration of bottom-up abstract interpretation using Figure 1 as an example (LP1 and LP2 are two lookup
paths of the function pointer fn at Line 7. LP2 is resolved at Step (6), leading to a function call to childProcess.exec.
Note that “Initial Stack” contains five functions that are in the file scope and pushed by FAST during initial scanning.)

32       async function compress(options) {                Control flow edge                                                                         anonymous array d                                                               options
           ENTRY                                              Intra-procedure
                                                  def   use
                                                              data flow edge
33         switch (options.alg) {
                                                           Selected statements                                                                            0                 1                2                    3             a
34           case options.alg == 'zip':
35              return await deflate(options);
36           case options.alg == 'xz':
                                                                                                                                                      literal               literal            literal
                                                                                                                                                 e                     f                 g                                  path             alg
37              var command = ['xz', '--stdout', '-k'];                                                                                               “xz”                  “-k”               “--stdout”
38              if (!options.path)                                                                                                                                                           literal “ ”
39                command.push("data");                                                                                                                                                        h             source i                   b
                                                                                                                                                                OP2




40              else
                                                                                                                                                                                    3
                                                                                                                                                                                   4
                                                                                                                                                                                OP
                                                                                                                                                                                OP




                                                                                                                                                                                                                          OP1               OP1
                                                                                                                                                                  OP




41                command.push(options.path);
                                                                                                                                                                                         OP5
                                                                                                                                                                     1




42              command = command.join(" ");                                                                                                #31 OP1 + OP4 +
                                                                                                                                            OP2 + OP4 + OP3                     j sink                      ns      B                   A OP1 == OP2 #25
43              logger.log(`xz, ${command}`);                                                                                                                                                     n relatio
                                                                                                                                                + OP4 + OP5                              conditio                                         result of case 'xz'
44              return await execProcess("exec")(command);                                                                                                                                                        !OP1
                                                                                                                                                 conditions                           conditions
45         }                                                                                                                                      A and !B                                                     #28 result of                OP2
                                                                                                                                                                                       A and !B
           EXIT                                                                                                                                                                                              !options.path
46       }                                                                                                                                                    arg                         command                                       c literal “xz”

Figure 6: An illustration of top-down abstract interpretation                                                                                        Generated constraints                                From the data flow path:
                                                                                                                                                                                                          (= e "xz")
of the compress of Figure 1 following control- and intra-                                                                                            From condition A:                                    (= f "-k")
                                                                                                                                                     (= b c)                                              (= g "--stdout")
procedural data-flow path.                                                                                                                           (= c "xz")                                           (= h " ")
                                                                                                                                                                                                          (= j (++ (++ (++ (++ (++
                                                                                                                                                     From condition !B:                                    (++ e h) f) h) g) h) i))
functional dependency graph (FDG), and generates accuracy                                                                                            (not (not ((not (= b "")))))                         (contains j "& touch exploit #")

call graph based on FDG. Specifically, FDG delays the                                                                                                 x       object node                           name node         source object node
                                                                                                                                                              x: variable used in constraints
challenging task of resolution of function calls until all the                                                                                                  name-object relation edge                  data flow edge
information is available, e.g., the value of an unresolved                                                                                                      data flow path            conditional data flow path
function pointer is passed as an argument of another function                                                                                                 sink object node                     conditional sink object node         # line number
call.
    We now describe FDGs and how bottom-up abstract                                                                                     Figure 7: Inter-procedural data-flow graph with control- and
interpretation creates FDG, call graphs and intra-procedural                                                                            data-flow constraints annotated of the motivating example.
control-flow graphs.
                                                                                                                                        resent different types of dependencies among those nodes.
Functional Dependency Graph (FDG). A functional depen-                                                                                  Given two nodes v1 and v2 in the graph, an edge (v1 , v2 ) rep-
dency graph is a graph whose nodes represent functions                                                                                  resents the fact that v2 is resolvable after v1 is resolved with
or function identifiers (e.g., pointers) and whose edges rep-                                                                           a call edge. The FDG captures in a concise way the different


                                                                                                                             6
ways in which JavaScript calls functions and the dependen-                         promisify
                                                                                            lookup                                          arrowFun2
cies between functions and function identifiers. This allows                 ret                        fn (Line 7)
FAST to accurately add call edges when the function identi-                                lookup                                 resolve
                                                                                                      callback                              Promise 1
fiers are resolved during abstract interpretation. In particular,        compress exec                                        new
                                                                                 Process               cb (Line 7)
FAST uses the paths in the FDG to propagate the resolution                                  resolve
                                                                                                                         main    then
                                                                                                                                            arrowFun3
of function calls when the callee is known. For instance,                                                                          new            callback
                                                                          anonymous          new       Promise
function executor in Figure 1 contains a function call                      (Line 5)                   (Line 6)               resolve
                                                                                                                                            Promise 2
via a function pointer fn in Line 7. The function pointer                                     await
                                                                                                                      arrowFun6     then            arrowFun4
is passed as a parameter to function promisify, which                                                  Line 44                              arrowFun7
in turn is called by execProcess. FDG models a de-
                                                                                           (a) Functional                     (b) Functional
pendency edge (called lookup dependency below) between                                  Dependency Graph of                Dependency Graph of
promisify/execProcess and executor. Then, when                                           Motivating Example                Then-chain Example
another function calls promisify/execProcess, FAST                                            arrowFun2
traverses dependency edges in FDG (i.e., Figure 8 (a)) to re-                       main                                    Functional Dependency Edge
solve fn and add corresponding call edges for executor.                                                                     Call Graph Edge
                                                                             Promise 1                arrowFun3
    We categorize FDG dependencies into four main types                     constructor                                     Function Node
covering all different scenarios in the ES6 specification [16].
                                                                             arrowFun1
                                                                                             Promise 2 arrow arrow arrow arrow
 • Lookup Dependency. A lookup dependency is caused                                         constructor Fun5 Fun6 Fun7 Fun4
  by a function pointer lookup (such as fn in Figure 1)                       (c) Call Graph of Then-chain Example
  in a closure or an outer scope where the pointer is
                                                                        Figure 8: An illustration of functional dependency graphs.
  used for invocation. These dependencies are represented
  by edges labeled with lookup in Figure 8 (a). Gener-                   1 const myPrms = new Promise((resolve, reject) => { //
  ally, a lookup dependency is determined by a lookup                       ,→ arrowFun1
                                                                         2   setTimeout(()=>{ // arrowFun2
  path (LP), which is defined as a series of lookups                     3      resolve("done");
  like a1[a2][a3]...[ak]. A lookup path can be                           4   }, 300)
  a straight line or a compound structure where each                     5 });
                                                                         6 myPrms.then(value => { // arrowFun3
  ak = b1[b2]...[bk]. We call a lookup path final                        7   setTimeout(()=>{}/*arrowFun4*/);
  when all objects that variables like ak and bk point                   8   return new Promise((resolve, reject) => {// arrowFun5
                                                                         9      setTimeout(()=>{ // arrowFun6
  to are either defined in a scope or passed as func-                   10         resolve(value);
  tion parameters. Then, FAST creates a lookup depen-                   11      }, 300)
  dency between the function pointer location and the                   12   })
                                                                        13 }).then(value => { //arrowFun7
  functions with the parameters. For example, Line 15                   14   console.log(value);
  of Figure 1 shows a relatively complex lookup path                    15 });
  childProcess[method] (which is also shown as                          Figure 9: An illustration of then chaining in Promise-
  LP2 in Figure 5) where childProcess is defined in                     related call graph construction (The global scope of this
  an outer scope at Line 2 and method is passed as a                    example is called “main” later in the paper).
  parameter at Line 14 of the execProcess function.
  FAST then creates a lookup dependency between Line 7                    the call edge of compress after execProcess is
  of executor function and execProcess at Line 14.                        analyzed.
 • Callback Dependency. A callback dependency (called                    • Promise Dependency. A promise dependency is caused
  a “trigger”) is caused by a callback function invoca-                   by a Promise object. FAST creates a special Promise
  tion, e.g., cb at Line 7 of Figure 1, where a function                  node in the functional dependency graph after the new
  is the parameter of another undecided or asynchronous                   operation and a “new” edge between the node and the
  function call. That is the undecided or asynchronous                    creator function. The created Promise node has in-
  function triggers this callback function. If the former                 coming dependencies from the functions that call re-
  is undecided, FAST will determine call edges after the                  solve and reject and outgoing dependencies caused
  former is resolved just like Line 7 of Figure 1; if the                 by then. Note that await is syntactic sugar of the
  former is asynchronous, FAST puts the invocation of                     then representation. That is, FAST will create a “then”
  latter callback after the former to the event queue of the              dependency edge to the statement immediately after the
  abstract interpreter because the callback is only registered            await statement.
  after the former’s execution.                                             To better illustrate Promise dependencies, we also
 • Return Dependency. A return dependency is caused by                  show a then chain example in Figure 9. The example
  an invocation of a function returned by another function.             creates a new Promise at Line 1 and then two then
  Line 44 of Figure 1 shows such an example: The return                 functions that are chained together at Line 6 and Line
  value of execProcess is invoked as a function at Line                 13. The example has seven arrow functions that are anno-
  44 with a parameter command. That is, FAST determines                 tated as comments in the figure. Figure 8 (b) shows the


                                                                    7
functional dependency graph of this then chain example.              backward slicing, (ii) top-down abstract interpretation and
The main scope creates Promise 1, which is resolved by               (iii) data-flow search and vulnerability detection.
arrowFun2. Then, Promise 1 triggers the then func-                       First, we describe intra-procedural backward slicing.
tion arrowFun3. arrowFun3 triggers arrowFun4 as an                   FAST generates intra-procedural data flow for each function
asynchronous function and also creates another Promise               and then performs a backward slicing based on the interme-
2. Promise 2 is resolved in arrowFun6 and triggers                   diate sink function, i.e., the next function call in the control-
arrowFun7.                                                           flow path, to skip unrelated statements. Let us look at our
Graph Creation. We describe how FAST uses bottom-up                  motivating example again. Figure 6 shows the backward
abstract interpretation to generate functional dependency,           slicing results (highlighted statements) of the compress
call edges, and intra-procedural control-flow edges, as well         function of our motivating example in Figure 1 following
as to resolve the dependencies. We describe the generation           a control-flow path leading to the final sink Line 7. We
based on different types of statements. A detailed algorithm         marked all the intra-procedural data-flow edges related to
can also be found in Appendix A.                                     the intermediate sink at Line 44: Anything unrelated to
                                                                     command (e.g. Line 43) or not on the control-flow path
 • Function calls. There are four types of function calls:           (e.g., Line 39) is filtered. This intra-procedural data-flow
  directly resolvable, pending, return-related, and callbacks        slice is used for our top-down abstract interpretation.
  (parameter-related). FAST adds corresponding call or                   Second, FAST follows a specific control-flow path and
  dependency edges to the FDG based on the type. If the              an intra-procedural slice selected based on the control-flow
  function is immediately resolvable, e.g., a direct function        path to abstractly interpret a subset of program statements.
  call, FAST adds the corresponding call edge. Otherwise,            Such a procedure is called a top-down abstract interpretation
  FAST adds a dependency edge and waits for the depen-               because it follows the call sequence, especially the caller-
  dent function for adding a call edge.                              callee relations. We describe two substeps of top-down
 • Function definitions. There are three types of function           abstract interpretation.
  definitions: callback, return function, and function ex-           Step 1. Object-level data-flow generation. First, FAST gen-
  pression. FAST pushes newly defined functions onto the             erates data flow between different objects (i.e., object-
  stack for further abstract interpretation. At the same time,       level data flows). Specifically, consider the following two
  FAST also tries to resolve functions that are dependent            statements: (1) p = a; and (2) o = p + b;. Both p
  on the newly defined function. For example, if a function          and a point to the same node, which solves the points-to
  is defined as a return value, FAST follows dependency              information. Then, FAST creates a data flow between the
  edges, finds its invocation location, and adds call edges.         node that p and a point to and the one that o points to. So
 • Promise-related statements. There are four types of               FAST does for o and b. The plus operator is also annotated
  Promise-related statements: new, then, await, and re-              atop of the object-level data-flow edge for the third stage to
  solve/reject. FAST adds dependency edges based on the              generate exploits. Note that similar data-flows are created
  statement type. If the statement is a resolve/reject, FAST         for template strings (e.g., ‘string${var}‘) and built-in
  will resolve the corresponding the corresponding Promise           function (e.g., Array.prototype.join) and operations
  and then trigger the “then” function if it is present.             are annotated on the edge as well.
                                                                     Step 2. Path-sensitivity information collection. FAST stores
    Having captured all the dependencies between possible            path-sensitivity information as an object in the object-level
function calls in the FDG, when FAST encounters depen-               data-flow graph and pushes the object onto a so-called
dency edges, it is able to execute a resolve-and-trigger             branch stack. Consider an if statement with a condition
strategy. In particular, once a single node is resolvable,           a && b. FAST creates an object node to denote the result
FAST will follow paths formed by dependency edges to                 of a && b that both object nodes of a and b have a
resolve all the pending call edges related to those paths.           data-dependency upon. Later on, when an object is created
Let us review our motivating example in Figure 1 and its             under a certain branch, the object is attached with a tag
functional dependency graph in Figure 8 (a) again. When the          that represents the current stack, i.e., all the path-sensitivity
parameter value of execProcess becomes available in the              related objects in the stack. Then, when FAST finishes
compress function, FAST resolves fn and then cb and                  the abstract interpretation of the branch, FAST pops the
then the Promise and await in a chain. Similarly, if we look         corresponding path-sensitivity object out of the branch stack.
at our then chain example in Figure 9, Figure 8 (c) shows                Lastly, FAST performs a data-flow path search to de-
the call graph generated from Figure 8 (b), where arrow-             termine the connectivity between sources and sinks. FAST
Fun2 triggers a chained call edge until arrowFun7.                   takes in input the list of sources and sinks and performs
                                                                     a Depth First Search (DFS) over the interprocedural DFG.
4.2. Stage II: Data-flow Path Generation
                                                                     The final result of this step is a set of source-sink data-flow
   In this stage, FAST finds a data-flow path between a              paths to indicate a possible vulnerability.
source and a sink following a specific control-flow path.
Details of such control-flow path discovery after bottom-            4.3. Stage III: Exploit Generation and Validation
up abstract interpretation can be found in Appendix B.                   The goal of this stage is to generate an exploit based on
We describe three components here: (i) intra-procedural              the extracted data-flow path and the detected vulnerability.


                                                                 8
If a path is exploitable, FAST considers the vulnerability            it adds the solution for a source object as the parameter
as exploitable. Otherwise, FAST repeats Stage I to try                to the function call. Second, FAST validates the generated
another control-flow path. Stage III is composed of three             exploit by running the exploit code. Take command injection
steps: type inference, constraint generation, and exploit code        for example. FAST checks whether an exploit file is created
generation.                                                           under the current directory if the exploit code is to touch a
Type Inference. One challenge in using constraint solv-               new file.
ing with is that of translating instructions into the lan-
guage of the constraint solver. Specifically, the main issue          4.4. Implementation
is that JavaScript is weakly and dynamically typed but                    We implemented FAST with 4,166 Lines of Code
constraint solvers (such as the Z3) are strongly, statically-         (LoC) in Python and 274 LoC in JavaScript. Our open-
typed. Therefore, when FAST generates constraints from                source implementation is available at this GitHub repository:
JavaScript, it also needs to provide type information to the          https://github.com/fast-sp-2023/fast. The abstract syntax tree
solver. To address this issue, FAST incorporates methods              (AST) generation is based on Esprima [17]. The graph repre-
for inferring variable types from known types. Particularly,          sentation reuses the graph component from the open-source
FAST uses two specific inference methods: forward and                 project ODGen [18] and the graph library NetworkX [19].
backward. Forward inference follows the data flow from an             The constraint solving is based on Z3 Theorem Prover [20],
object to its uses in built-in functions and derives the type         which includes Z3-str, now an official component of Z3.
based on the specific built-in. For example, if an object is          Note that all third-party code is excluded from the above
used in childProcess.exec, FAST can infer that this                   LoC.
object is a string type. Second, backward inference is that
FAST follows the data-flow in backward from an object and             5. Evaluation
iterates through all the objects related to the object in the            Our evaluation answers five Research Questions (RQs):
data flow. For example, say, FAST is inferring the type of
b in b = a + "str". When FAST goes backward and                        • RQ1 [Zero-day]: How many zero-day vulnerabilities can
finds that “str” is of a string type, FAST then infers both a           FAST detect but state-of-the-art approaches cannot?
and b are of a string type for the solver.                             • RQ2 [FP&FN]: What are FAST’s false negatives (FNs)
Constraint Generation. The second step is to generate                   and false positives (FPs) in detecting vulnerabilities?
constraints from the data-flow path extracted from Stage               • RQ3 [Scalability]: How scalable is FAST in detecting
II. We classify constraints in FAST into three categories.              vulnerabilities in large-scale packages?
(i) Sink object constraints, which are converted from the              • RQ4 [Call Graph]: How many new call graph edges can
sensitive sink object, e.g., parameters of the sink func-               FAST generate compared with state of the art?
tion. Such constraints have two parts: constraints on the
sink object itself, and constraints on the sink object and
                                                                      5.1. Experimental Setup
source objects. The former is vulnerability specific: for             Datasets. We collect and form three datasets. (i) Real-
example, if the vulnerability is command injection, FAST              world Node.js packages with the first 100,000 NPM Node.js
may add a constraint based on a vulnerable dictionary like            packages ranked by number of dependencies. (ii) Vulner-
(str.contains o "; touch exp #"). The latter                          ability benchmark with 391 vulnerable Node.js packages
is based on a backward traversal of the sink object in the            with 391 taint-style vulnerabilities from three types, i.e.,
data-flow graph to reach sources. (ii) Path constraints, which        OS command injection, arbitrary code execution and path
are converted from path objects stored in the branch stack            traversal. The packages in this benchmark come from the
as discussed in Section 4.2. FAST loops through all the               ODGen repository [18], the Nodest paper [10], and legacy
objects in the stack and generates such constraints. The              CVEs in 2021 and 2022 (which is after the ODGen paper).
generation process is similar to sink object without the              (iii) Scalability benchmark with 13 vulnerabilities in eight
vulnerability-specific constraint. (iii) Constant constraints,        packages-version pairs with more than 10K LoC (excluding
which are generated during the former two when FAST                   third-party code). We collect this dataset by surveying pop-
can determine the value of a certain object from a constant           ular CMSes [21] in JavaScript and finding their in-scope,
value. A detailed algorithm can be found in Appendix C.               taint-style vulnerabilities with confirmed exploit code.
Exploit Code Generation. The third step is to generate                Experimental Environment. All our experiments are per-
exploit code based on the constraints extracted from the              formed on a server with 192 GB memory and Intel Xeon E5-
second step. FAST feeds all the constraints into a solver             2690 v4 2.6GHz CPU with 14 cores. We run 16 threads of
(such as Z3) and obtains a solution. Then, the next step is to        FAST at the same time for the real-world Node.js packages
generate an exploit code, which has two sub-steps: function           to speed-up the analysis. We evaluate the following tools
call preparation and exploit validation. First, when the solver       in our experiment. First, there are two variations of FAST:
gives values for each source object, FAST needs to first              FAST-det and FAST-exp. FAST-det, the default version
find the correct way to call the function. Specifically, FAST         of FAST, detects a vulnerability if a data-flow path is
finds the definition of the function object and then searches         found between a source and a sink. FAST-exp reports a
through its parent object (e.g., parent.child) until it               vulnerability found by FAST-det as exploitable if it can
finds an external object, such as module.exports. Next,               successfully generate an exploit. Second, we also include


                                                                  9
TABLE 2: [RQ1] A breakdown of confirmed zero-day                              TABLE 4: [RQ2-FN] False negative comparison on
vulnerabilities found by FAST but not state-of-the-art ap-                    vulnerability benchmark between two variations of FAST,
proaches (SOTAs), i.e., neither ODGen [8] nor CodeQL [7]                      ODGen [8] and CodeQL [7] on vulnerability benchmark.
detects them, on 100k real-world Node.js packages.
                                                                                         Cmd Injection Code Execution Path Traversal    Total
  Vulnerability           FAST-det&      FAST-exp&           FAST-det&
                           ¬SOTA           ¬SOTA               SOTA                       TP     FN      TP     FN      TP     FN      TP FN
  Command Injection          113               92               177           FAST-det    169     18     42      12     115    35      326 65
  Arbitrary Code Exec.        68               39                29           FAST-exp     86    101     13      41      65    85      164 228
  Path Traversal              61               51                24
                                                                              ODGen       107     80     24      30      89    61      220 171
  Total                      242               182              230
                                                                              CodeQL      122     65     21      33     110    40      253 138

TABLE 3: [RQ2] False Positive and Negative Rate Com-
parison between FAST and ODGen.                                               it contains non-vulnerable packages and we do not have any
                         FAST-det     FAST-exp ODGen CodeQL                   ground truth).
  False Negative Rate     16.6%        58.3%         43.7%     35.3%               On one hand, FAST-det outperforms all SOTAs with
  False Positive Rate     11.8%         0%           23.3%     27.8%          the lowest FP and FN rates. FAST-det outperforms ex-
                                                                              isting abstract interpretation (i.e., ODGen) because of our
                                                                              improvement on scalability. FAST-det outperforms existing
two state-of-the-art (SOTA) tools: (i) ODGen [8], the SOTA                    syntax-driven approaches (e.g., CodeQL) because abstract
abstract interpretation tool, and (ii) CodeQL [7], the SOTA,                  interpretation can solve dynamic JavaScript features like
industry-level syntax-directed tool.                                          dynamic object lookups using bracket syntax. On the other
                                                                              hand, FAST-exp has zero false positives but relatively high
5.2. RQ1: Zero-day Vulnerabilities                                            false negatives because it generates exploits by solving all
    In this subsection, we answer the research question on                    the constraints. In many cases, FNs are because Z3-solver
how many zero-day vulnerabilities FAST can detect while                       does not come up with a solution while our human being
four SOTA approaches (mentioned in our experimental                           can solve them manually. Note that we count packages
setup) cannot. We run all the tools upon our real-world                       with intended functionalities as true positives of analysis
Node.js packages. Then, we consider a detected vulnerabil-                    but not zero-day vulnerabilities because we are calculating
ity as zero-day if a human expert confirms the vulnerability                  true positives of our program analysis, which is performing
with a generated exploit and we cannot find any information                   correctly. The total number is also small, i.e., only nine
about the vulnerability online. We also have responsibly                      arbitrary code execution among all vulnerable packages.
reported all zero-day vulnerabilities to corresponding de-                    False Negative Breakdown. Table 4 shows a breakdown
velopers and gave them 45 days for fixes. So far we have                      of false negatives of different tools. FAST-det outperforms
obtained 21 CVE identifiers; we anonymize them for the                        existing works in all vulnerability categories. The main
purpose of double-blind submission. Table 2 shows a list of                   reason of FN for FAST-det is that there are some unmodeled
zero-day vulnerabilities that is broken down by vulnerability                 sources or sinks, leading to missing data flow. ODGen’s FNs
type. In total, FAST detects 242 zero-day vulnerabilities and                 are mainly because of code coverage, i.e., much vulnerable
exploits 182 of them.                                                         code may not be even reached during the analysis. CodeQL’s
A Case Study.              We use fastboot-gcloud-storage-                    FNs are due to dynamic JavaScript features, such as function
downloader@1.0.0., which is a downloader for the FastBoot                     calls related to bracket syntax.
App Server to download and unzip deployed applications                        False Positive Breakdown. Table 5 shows a breakdown
from Google Storage, as an example. The package uses                          of FAST’s false positives by vulnerability types and its
“exec” to download and unzip deployed applications but                        comparison with SOTAs. FAST outperforms SOTAs on all
fails to sanitize inputs potentially controlled by an adversary,              types of vulnerabilities. The main reason of FPs of FAST-
thus leading to an OS command injection vulnerability.                        det is that many applications contain either control- or data-
FAST-det successfully detects this package as vulnerable                      flow sanitizations, which make the detected vulnerability
and then FAST-exp automatically generates an exploit. By                      unexploitable. This also shows that we need FAST-exp to
contrast, neither ODGen nor CodeQL detects this vulnera-                      help the exploitation. As a comparison, CodeQL’s FPs are
bility because of the heavy use of Promise and template                       higher than FAST-det because there are over-approximations
string, leading to missing control- or data-flow paths.                       of control- and data-flows due to lack of abstract interpre-
                                                                              tation in a syntax-driven approach.
5.3. RQ2: False Negatives and Positives
Overview. Table 3 shows an overview of the comparison
                                                                              5.4. RQ3: Scalability
between all four approaches. False negatives (FNs) are                            In this subsection, we evaluate the scalability of FAST in
evaluated on the vulnerability benchmark (because we have                     detecting vulnerabilities of the scalability benchmark. There
the ground truth information) and false positives (FPs) on the                are two things worth noting here. First, although there is
first 10K Node.js packages in the real-world dataset (because                 only one CVE identifier for strapi@4.0.8, there are two


                                                                         10
TABLE 5: [RQ2-FP] False positives of two variations of                                      finish times of both FAST-det and FAST-exp increase. The
FAST in analyzing 10k real-world Node.js packages.                                          increase is linear as we show the trend in a line fit (both x-
              Cmd Injection Code Execution Path Traversal                      Total
                                                                                            and y-axes are in log scale). The finish time of FAST-exp
                                                                                            is slightly higher than FAST-det because of the additional
                TP        FP               TP        FP       TP       FP      TP FP        exploitation time.
FAST-det         56           4            16         5            3    1      75 10            We also show a cumulative distributional function (CDF)
FAST-exp         35           0             6         0            3    0      44 0         graph of the performance overhead of both FAST and
ODGen            17           5            13         5            3    0      33 10        FAST-det on our vulnerability benchmark in Figure 11.
CodeQL           52           13           12         8            1    4      65 25        The median performance overheads are 26.3 seconds and
TABLE 6: [RQ3] Detection and exploitation status of FAST                                    31.6 seconds for FAST-det and FAST-exp respectively.
and ODGen of the scalability dataset (>10K LoC).                                            There are three things worth noting here. First, FAST
                                                                                            finishes analyzing most packages with one minute. Second,
 Package       Version             LoC             Vulnerability       FAST ODGen           the performance overheads of FAST-det and FAST-exp
 strapi         4.0.8          196,338           CVE-2022-0764†         2/2     0/2         are similar, i.e., exploit generation is relatively fast. Lastly,
 strapi     3.0.0-beta.17.7       85,520        CVE-2019-19609     †
                                                                        2/2     0/2
                                                                                            the largest overhead is 3,401 seconds (almost an hour) for
                                                                                            FAST-exp (not shown in figure) in analyzing api@0.15.9
                                            GHSA-wfrj-qqc2-83cm‡        1/1     0/1         with 12K Lines of Code because of the heavily uses of
 ghost          4.3.0             71,041
                                              CVE-2021-29484            0/1     0/1
                                                                                            dynamic calls. We also have a performance breakdown by
 NodeBB         1.4.0             70,950    npm:nodebb:20161120‡        1/1     0/1
                                                                                            stages in Appendix D.
 NodeBB         0.6.1             46,092    npm:nodebb:20150413‡        1/1     0/1
                                                                                            5.5. RQ4: Call Edges
                                                 CVE-2020-28494         1/1     0/1
                                                 CVE-2021-23344         1/1     0/1              In this research question, we compare call edges pro-
 total.js       3.4.5             40,593                                                    duced by FAST and existing approaches, namely the open-
                                                                   ∗
                                                CVE-2021-23389
                                                                        1/1     0/1         source implementations of (i) ODGen [8], [18], an abstract
                                                CVE-2021-32831∗
                                                 CVE-2019-8903          0/1     0/1
                                                                                            interpretation approach, and (ii) JS Call Graph [23], [24],
 total.js       3.2.2             33,109
                                                 CVE-2019-10260         0/1     0/1         a syntax-directed approach. Note that we choose JS Call
 hapi           0.15.9            12,681        npm:hapi:20130320‡      1/1     0/1         Graph because some follow-up works are either entirely
                                                                                            closed source [14] or does not provide a call graph for
 Total            –                 –                     –            11/14   0/14
                                                                                            comparison [7].
†: The CVE maps to two vulnerabilities with two sinks and two different exploits.                Our methodology is as follows. We run all three ap-
‡: We use snyk-id because there are no CVE identifiers.
∗: These two CVEs map to the same vulnerability.                                            proaches on our vulnerability benchmark, produce call edges
                                                                                            and then compare the results produced by three approaches.
vulnerable sinks and two different exploits, i.e., two vulnera-                             We then manually inspect all the edges produced by three
bilities. The same applies to strapi@3.0.0-beta.17.7. Second,                               approaches for correctness. The inspection of all the edges
interestingly, although there are two CVE identifiers (CVE-                                 takes a graduate student approximately 230 hours. Lastly, we
2021-23389 and CVE-2021-32831) for total.js@3.4.5, there                                    show the breakdown of false positive and negative edges of
is only one vulnerability. Note that these two CVEs are                                     each approach.
follow-ups of CVE-2021-23344, because the patch of CVE-                                          First, Table 7 shows false positives and negatives of call
2021-23344 is also vulnerable.                                                              edges produced by all three approaches. FAST outperforms
     Table 6 shows the detection results of FAST and                                        both ODGen and JS Call Graph (JSCG) in terms of FPs
ODGen. FAST is able to detect ten out of 14 vulnerabilities,                                and FNs. Let us start from FPs, i.e., incorrect call edges.
even in strapi with almost 200K LoC. By contrast, ODGen                                     The FPs of JS Call Graph are the highest because it adopts
detects none of these vulnerabilities after analyzing each                                  a syntax-based, name-driven matching. Therefore, JS Call
vulnerability for one day (sometimes it may crash). FAST                                    Graph often mismatches a function call to a definition under
also misses the detection of three vulnerabilities. The main                                an incorrect scope. Say there are two functions called foo
reason is the lack of modeling of corresponding sources or                                  under different scopes and JS Call Graph often chooses
sinks by FAST. Take CVE-2021-29484 [22] in ghost@4.3.0                                      a wrong one. By contrast, the FPs of ODGen and FAST
for example. It is client-side vulnerability starting from a                                are relatively smaller. The main reasons are unsupported
postMessage channel as a source, which was not mod-                                         features. For example, if FAST cannot recognize whether
elled by FAST. The two vulnerabilities of total.js@3.2.2                                    a callback function is synchronous or asynchronous, FAST
are similar. We do not find sources and sinks modeled by                                    will default it as synchronous, leading to potential FPs.
FAST that are related to the vulnerabilities. Furthermore,                                       We then discuss FNs, i.e., missing call edges. ODGen
we cannot reproduce either vulnerability, which prevents us                                 misses many call edges because of code reachability in the
from understanding the real source or sink.                                                 analysis. Specifically, ODGen often has an exponential num-
     At the same time, we also show the total finish time of                                ber of nodes during analysis, leading to a scalability issue
FAST vs. the number of Abstract Syntax Tree (AST) Nodes                                     as shown in the motivating example of Figure 1. The FNs of
of our scalability and vulnerability benchmark combination                                  JS Call Graph are also mostly caused by scope mismatch,
in Figure 10. When the number of AST node increases, the                                    i.e., it chooses the wrong function under a different scope.


                                                                                       11
                     5
                   10
                            FAST-det                                                                                                             FAST-det




                                                                                        Percentage of finished packages [%]
                            FAST-det line fit                                                                                                    FAST-exp
                     4      FAST-exp                                                                                          100
                   10
                            FAST-exp line fit                                                                                  90
                   103                                                                                                         80
 Finish time [s]




                                                                                                                               70
                   102                                                                                                         60
                                                                                                                               50
                   101                                                                                                         40
                                                                                                                               30
                   100                                                                                                         20
                                                                                                                               10
                   10-1 2                                                                                                       0
                       10   103          104         105          106                                                               0      100        200         300       400        500      600
                                     Number of AST nodes                                                                                                    Finish time [s]
Figure 10: [RQ3] Detection Finish Time vs. the Number of                              Figure 11: [RQ3] Cumulative Distribution Function (CDF)
Abstract Syntax Tree (AST) Nodes on a Combination of                                  of Performance Overhead on a Combination of Vulnerability
Vulnerability and Scalability Benchmarks                                              and Scalability Benchmarks
TABLE 7: [RQ4] Call edge breakdown of ODGen, JS Call
Graph, and FAST (%Edges for True Positives: TP/(TP+FP);                                                                                                      #: 1,436
                                                                                                                                                            TPR: 89.6%
%Edges for False Positives: FP/(TP+FP); %Edges for False
Negatives: FN/(TP+FN) )
                                  ODGen         JS Call Graph         FAST                                                           #: 2,466
                                                                                                                                    TPR: 84.6%
                             #Edges %Edges #Edges %Edges #Edges %Edges                                                                                #: 1,825            #: 1,635
                                                                                                                                                     TPR: 100%           TPR: 100%
True Positives               4,137     89.4%    3,617   78.6%   6,831    92.8%            #: 319
                                                                                        TPR: 64.9%                                                  #: 19                          #: 1,123
False Positives               492      10.6%    985     21.4%   531      7.2%
                                                                                                                                                  TPR: 94.7%                      TPR: 12.3%
  Scope mismatch               0        0%      985     21.4%     0       0%
  Unsupported feature         380      8.2%      0       0%     479      6.5%          ODGen                                                                FAST                       JS Call Graph
  Implementation bug          112      2.4%      0       0%      52      0.7%          #: 4,629                                                             #: 7,362                   #: 4,602
                                                                                       TPR: 89.4%                                                           TPR: 92.8%                 TPR: 78.6%
False Negatives              3,059     42.5%    3,579   49.7%   365      5.1%
  Reachability               1,824     25.3%      0       0%     0        0%
  Function pointers            0         0%      280     3.9%    0        0%
                                                                                      Figure 12: A Venn diagram showing the overlaps among call
  Scope mismatch               0         0%     2,190   30.4%    0        0%          edges produced by three approaches (TPR: TP/(TP+FP)).
  Unknown objects             137       2.0%      0       0%    137      2.0%
  Implementation bug         1,098     15.2%    1,109   15.4%   228      3.1%         CVE Numbering Authority (CNA) to not only obtain CVE
                                                                                      identifiers but also contact corresponding developers for
                                                                                      fixes. Our practice follows industry standard in vulnerability
There are two reasons of FNs for FAST. On one hand,                                   disclosure [25] and our organization’s policy.
FAST cannot create call edges for an unknown object, e.g.,
one passed through a function parameter without a formal                              Loops. Loops are also a major challenge leading to scal-
definition. On the other hand, our current implementation                             ability issues in prior works. FAST is able to reduce the
still has an engineering bug in creating call edges for some                          number of abstractly interpreted loops due to two reasons.
function calls in embedded ternary operator. We will fix this                         First, the bottom-up abstract interpretation only analyzes
bug in the future.                                                                    loops that are related to function calls, e.g., function pointer
     Second, we also show a Venn graph of all the edges                               lookups and invocations in a loop, thus skipping many loops
produced by three approaches in Figure 12. On one hand, the                           related to data operations. Second, the top-down abstract
overlaps between ODGen and FAST are large because both                                interpretation only analyzes loops that have control- or data-
are based on abstract interpretation. The missing part from                           dependencies with the sink, thus skipping those that do
ODGen, as described, is mostly because of reachability.                               not. The loop analysis follows two strategies: if the looping
On the other hand, JS Call Graph has many unique edges                                number is known (e.g., a constant array), FAST extensively
compared with FAST and ODGen. The false positive rate                                 loops through every element; if unknown, FAST uses a
for the unique edges is very high and the main reason is                              threshold, i.e., three, for the loop.
scope mismatch as described above.                                                    Vulnerability Exploitation. The purpose of FAST-exp is
                                                                                      to filter packages that can be automatically exploited, thus
6. Discussion                                                                         reducing human efforts in confirming vulnerabilities. The
Ethics. We have contacted vulnerable Node.js package de-                              current implementation can reduce the amount of human
velopers and given them 45 days for a fix if we can find                              works by about half, while still leaving the rest as human
their contact. At the same time, we are also working with a                           work. The major reason of failures is that Z3 solver fails


                                                                                 12
to produce a solution and times out based on provided                   find specification-driven bugs. FAST-exp is a static sym-
constraints, but a human being can come up with a solution              bolic execution engine and it is the first that generates
with the constraints. We leave this as our future work.                 exploit code statically for JavaScript vulnerability.
Analysis Soundness. While FAST significantly improves                   Client-side JavaScript Security. We also start from dy-
the scalability of existing abstract interpretation, we would           namic analysis. Melicher et al. [47] and Steffens et al. [48]
like to point out that FAST—just like all existing static               both use dynamic taint analysis to find DOM-based XSS.
analysis—is unsound [15]. Our manual inspection shows                   Deemon [49] adopts dynamic analysis and property graphs
that unsoundness, particularly False Negatives, is primarily            to detect CSRF vulnerability. CSPAutoGen [50] enforces a
caused by three reasons in practice: (i) lack of model-                 template following Content Security Policy to defend against
ing of built-in functions (>90%), (ii) AST parsing errors               client-side XSS. PathCutter [51] cuts off the propagation
from Esprima (e.g., public class field that is supported by             paths of XSS worms. Black Widow [52] introduces a black
many browsers and Node.js [26] and to be included in                    box data-driven approach to crawl and scan web applica-
ES2023 [27]), and (iii) the pruned path in the second phase             tions. JSObserver [53] investigates the client-side JavaScript
is still heavyweight to analyze. In theory, such unsound-               code integrity problem caused by JavaScript global identi-
ness may also be caused by dynamically introduced code                  fier conflicts. Next, we describe static analysis. JStap [5],
especially when user inputs are involved. At the same time,             HideNoSeek [54], JaSt [55] and JShield [56], [57] adopt
we would like to point out that functions related to dy-                signature matching or static analysis to detect malicious
namic code are often sinks of taint-style vulnerabilities (e.g.,        JavaScript programs. DoubleX [6] analyzes the taint flow
eval [28], [29] for arbitrary code execution). Therefore,               to detect browser extension vulnerabilities. JSIsolate [58]
such unsoundness in call graph construction often does not              uses the dependency relationship of different components
affect FAST’s ability in detecting vulnerabilities.                     of the JavaScript programs to prevent the functionalities
                                                                        from interfering with each other. COP [59] proposes a
7. Related works                                                        configurable origin policy to isolate JavaScript in a more
    We discuss the related work in this section.                        fine-grained pattern. Cao et al. [60] studied a new protocol
Node.js Vulnerability Detection. On one hand, we start                  of single sign-on for client-side JavaScripot. New browser
from dynamic analysis. Jalangi [30] uses a selective record-            architectures, such as virtual browser [61] and deterministic
replay method to analyze front- and back-end JavaScript                 browser [62], have also been proposed. JAW [63] models
programs dynamically. Arteau [31] proposes a dynamic                    browser objects in a Hybrid Property Graph for client-side
fuzzer to detect prototype pollution vulnerabilities. On the            CSRF vulnerabilities. Researchers have also studied client-
other hand, we describe static analysis. ODGen [8] proposes             side browser fingerprints [64]–[66] or web tracking [67] in
object dependence graph to detect vulnerabilities based on              general. As a comparison, the target of FAST, i.e., Node.js
graph queries. DAPP [32] uses AST and control-flow pat-                 vulnerability, is different from prior works.
terns to detect prototype pollution vulnerabilities. ObjLu-                  Some existing works [68]–[72] adopt Automated Exploit
pAnsys [11] detects prototype pollution vulnerabilities by              Generation (AEG) to exploit client-side XSS vulnerabil-
expanding and mapping two clusters during the abstract                  ities based on dynamically collected traces or dataflows.
interpretation. Nodest [10], a project based on TAJS [12],              Kudzu [44] uses dynamic symbolic execution and a con-
introduces an efficient method to detect command injec-                 straint solver to detect and exploit client-side XSS and code
tion vulnerability. Both Ocular [33] and CodeQL [7] are                 injection vulnerabilities. Song et al. [73] and Kang et al. [74]
industry-level, graph query-based vulnerability detection               exploit the underlying JIT compiler, instead of JavaScript
tool. As a comparison, FAST scales to large, complex                    itself, which could be applied to other JIT-compiled lan-
Node.js applications to detect taint-style vulnerabilities.             guages.
    Other than taint-style vulnerabilities, in the past, re-            JavaScript Static Analysis Frameworks. TAJS [12] and
searchers have studied various security issues or non-taint-            JSAI [75] adopt abstract interpretation to analyze JavaScript
style vulnerabilities in the Node.js eco-systems, which in-             programs for type inference. SAFE [9] and its follow-up
clude supply chain security [34], [35], Regular Expres-                 work SAFEWAPI [76] covert JS to an Intermediate Rep-
sion Denial of Service (ReDoS) [36]–[38], privilege reduc-              resentation (IR) for abstract interpretation. PageGraph [77]
tion [34], debloating [39], hidden property abuse [40], and             and AdGraph [78] model the relations between different
prototype pollution [41]–[43]. As a comparison, FAST is                 browser objects. SAFEDS [79] adopts Jalangi, a dynamic
targeting a different problem from those work, and may be               analysis tool, to build dynamic shortcuts on top of SAFE
able to help them in the future if static analysis is used.             to accelerate the static analysis to large packages such as
JavaScript Symbolic Execution. JavaScript symbolic exe-                 official tests of Lodash. As a comparison, FAST does not
cution also has two general types: dynamic [44], [45] and               need any dynamic execution, which need setup of both
static [46]. On one hand, dynamic symbolic execution, such              inputs and environments to deploy. Furthermore, none of
as ExpoSE [45], relies on an existing JavaScript engine,                these frameworks are used for vulnerability detection or
to propagate symbolic values. On the other hand, static                 exploitation.
symbolic execution, such as Cosette [46], uses a symbolic                   JavaScript call graph construction [80]–[85] has been
interpreter to propagate symbols and extract constraints to             studied for a long time, which may use static [81], dy-


                                                                   13
namic [82], or hybrid [80] analysis. For example, Nielsen et             well as an Amazon Research Award (ARA) 2021. The views
al. [14] scan Node.js application to construct modular (e.g.,            and conclusions contained herein are those of the authors
inter-file) call graph graph. Feldthaus et al. [23] design field-        and should not be interpreted as necessarily representing
based flow analysis for constructing call graphs. Existing               the official policies or endorsements, either expressed or
static call graph construction traditionally faces challenging           implied, of NSF, DARPA, or Amazon.
issues for dynamic features, such as bracket syntax and
Promise. Existing dynamic call graph construction often
faces issues like code coverage and practical deployment                 References
(e.g., some Node.js packages may not run without a proper                [1]   K. Cheng, Q. Li, L. Wang, Q. Chen, Y. Zheng, L. Sun, and Z. Liang,
environment setup). Hybrid analysis leverages benefits of                      “Dtaint: Detecting the taint-style vulnerability in embedded device
both static and dynamic analysis but also inherits drawbacks                   firmware,” in 2018 48th Annual IEEE/IFIP International Conference
                                                                               on Dependable Systems and Networks (DSN), 2018, pp. 430–441.
of both. As a comparison, FAST is the first static abstract
                                                                         [2]   “Static exploration of Taint-Style vulnerabilities found by fuzzing,”
interpretation based call graph construction, which tackles                    in 11th USENIX Workshop on Offensive Technologies (WOOT 17).
call edges related to many dynamic JavaScript features.                        Vancouver, BC: USENIX Association, Aug. 2017. [Online]. Avail-
                                                                               able: https://www.usenix.org/conference/woot17/workshop-program/
Vulnerability Detection or Program Analysis Techniques.                        presentation/shastry
Yamaguchi et al. introduce Code Property Graph (CPG) [86]
                                                                         [3]   F. Yamaguchi, A. Maier, H. Gascon, and K. Rieck, “Automatic
to detect C/C++ vulnerabilities. Built upon CPG, Backes et                     inference of search patterns for taint-style vulnerabilities,” in 2015
al. [87] adapt CPG to PHP to detect PHP vulnerabilities.                       IEEE Symposium on Security and Privacy, 2015, pp. 797–812.
Randoop [88] produces unit tests for Java via feedback-                  [4]   C.-A. Staicu, M. Pradel, and B. Livshits, “SYNODE: Understand-
directed random test generation. Program slicing [89], a con-                  ing and automatically preventing injection attacks on NODE.JS,” in
cept proposed in 1980s, has been widely used for program                       NDSS, 2018.
analysis and vulnerability detection. Previous works [90],               [5]   A. Fass, M. Backes, and B. Stock, “JStap: A static pre-
[91] proposed to use abstract interpretation to facilitate                     filter for malicious JavaScript detection,” in Proceedings of
                                                                               the 35th Annual Computer Security Applications Conference,
program slicing. As a comparison, the pruning process, i.e.,                   ser. ACSAC ’19. New York, NY, USA: Association for
program slicing adopted by FAST is used to scale abstract                      Computing Machinery, 2019, p. 257–269. [Online]. Available:
interpretation.                                                                https://doi.org/10.1145/3359789.3359813
                                                                         [6]   A. Fass, D. F. Somé, M. Backes, and B. Stock, “DoubleX: Statically
8. Conclusion                                                                  detecting vulnerable data flows in browser extensions at scale,” in
                                                                               Proceedings of the 2021 ACM SIGSAC Conference on Computer
    In this paper, we propose a novel two-phase abstract                       and Communications Security, ser. CCS ’21. New York, NY,
interpretation, called FAST, for detection and exploita-                       USA: Association for Computing Machinery, 2021, p. 1789–1804.
tion of Node.js taint-style vulnerabilities. The first phase                   [Online]. Available: https://doi.org/10.1145/3460120.3484745
(bottom-up abstract interpretation) generates a control-flow             [7]   GitHub. CodeQL. https://codeql.github.com/.
path between source and sink. Then, the second phase (top-               [8]   S. Li, M. Kang, J. Hou, and Y. Cao, “Mining Node.js
down abstract interpretation) follows the control-flow path to                 vulnerabilities via object dependence graph and query,” in 31st
                                                                               USENIX Security Symposium (USENIX Security 22). Boston,
only analyze statements with control- and data-dependencies                    MA: USENIX Association, Aug. 2022. [Online]. Available: https:
with the sink. Compared with state-of-the-art abstract in-                     //www.usenix.org/conference/usenixsecurity22/presentation/li-song
terpretation, such a pruned analysis significantly reduces               [9]   H. Lee, S. Won, J. Jin, J. Cho, and S. Ryu, “SAFE: Formal spec-
the states in the abstract domain and scales the analysis.                     ification and implementation of a scalable analysis framework for
After two phases, FAST also collects and solves data- and                      ECMAScript,” in International Workshop on Foundations of Object-
control-flow constraints along the target control-flow path to                 Oriented Languages (FOOL), vol. 10. Citeseer, 2012.
automatically generate exploits. Our evaluation shows that               [10] B. B. Nielsen, B. Hassanshahi, and F. Gauthier, “Nodest: Feedback-
                                                                              driven static analysis of node.js applications,” in Proceedings of the
FAST outperforms the state-of-the-art approach in reducing                    2019 27th ACM Joint Meeting on European Software Engineering
false negatives and detects 242 zero-day vulnerabilities with                 Conference and Symposium on the Foundations of Software Engi-
21 CVE identifiers.                                                           neering (ESEC/FSE), 2019, p. 455–465.
                                                                         [11] S. Li, M. Kang, J. Hou, and Y. Cao, “Detecting Node.js prototype
Acknowledgement                                                               pollution vulnerabilities via object lookup analysis,” in ESEC/FSE
                                                                              ’21: 29th ACM Joint European Software Engineering Conference and
    We would like to thank Isaac Chang, Jianjia Yu, Jun-                      Symposium on the Foundations of Software Engineering, 2021.
min Zhu, and Zhengyu Liu for their help with manual                      [12] S. H. Jensen, A. Møller, and P. Thiemann, “Type analysis for
verification and exploitation of zero-day vulnerabilities. We                 JavaScript,” in Proc. 16th International Static Analysis Symposium
also would like to thank Snyk for vulnerability disclosure                    (SAS), ser. LNCS, vol. 5673. Springer-Verlag, August 2009.
and CVE assignment, and anonymous reviewers for their                    [13] Promise - JavaScript — MDN. https://developer.mozilla.org/en-US/
helpful comments and feedback. This work was supported                        docs/Web/{JavaScript}/Reference/Global Objects/Promise.
in part by National Science Foundation (NSF) under grants                [14] B. B. Nielsen, M. T. Torp, and A. Møller, “Modular call graph
CNS-21-54404 and CNS-20-46361 and Defense Advanced                            construction for security scanning of node.js applications,” in
                                                                              Proceedings of the 30th ACM SIGSOFT International Symposium
Research Projects Agency (DARPA) under AFRL Definitive                        on Software Testing and Analysis, ser. ISSTA 2021. New York,
Contract FA875019C0006 and a DARPA Young Faculty                              NY, USA: Association for Computing Machinery, 2021, p. 29–41.
Award (YFA) under Grant Agreement D22AP00137-00 as                            [Online]. Available: https://doi.org/10.1145/3460319.3464836



                                                                    14
[15] F. Al Kassar, G. Clerici, L. Compagna, F. Yamaguchi, and                           [38] J. C. Davis, E. R. Williamson, and D. Lee, “A sense of time for
     D. Balzarotti, “Testability tarpits: the impact of code patterns on the                 JavaScript and Node.js: First-class timeouts as a cure for event handler
     security testing of web applications,” 2022.                                            poisoning,” in 27th USENIX Security Symposium (USENIX Security
[16] ECMAScript       2015      language         specification.     https://262.             18), 2018, pp. 343–359.
     ecma-international.org/6.0/.                                                       [39] I. Koishybayev and A. Kapravelos, “Mininode: Reducing the attack
[17] Esprima: ECMAScript parsing infrastructure for multipurpose analy-                      surface of Node.js applications,” in 23rd International Symposium on
     sis. https://esprima.org/.                                                              Research in Attacks, Intrusions and Defenses (RAID 2020), 2020, pp.
                                                                                             121–134.
[18] S. Li. ODGen source code. https://github.com/Song-Li/ODGen/.
[19] NetworkX: Network analysis in python. https://networkx.org/.                       [40] F. Xiao, J. Huang, Y. Xiong, G. Yang, H. Hu, G. Gu, and W. Lee,
                                                                                             “Abusing hidden properties to attack the Node.js ecosystem,” in 30th
[20] Z3 thereom prover. https://github.com/Z3Prover/z3.                                      USENIX Security Symposium (USENIX Security 21), 2021, pp. 2951–
[21] N. James. Best Node.js CMS platforms for 2022. https://blog.                            2968.
     logrocket.com/best-node-js-cms-platforms-2022/.
                                                                                        [41] M. Shcherbakov, M. Balliu, and C.-A. Staicu, “Silent Spring: Proto-
[22] P. Gerste. Ghost CMS 4.3.2 - cross-origin admin takeover. https://                      type pollution leads to remote code execution in Node.js,” 2023.
     blog.sonarsource.com/ghost-admin-takeover.
                                                                                        [42] Z. Kang, S. Li, and Y. Cao, “Probe the Proto: Measuring client-
[23] A. Feldthaus, M. Schäfer, M. Sridharan, J. Dolby, and F. Tip,                          side prototype pollution vulnerabilities of one million real-world
     “Efficient construction of approximate call graphs for JavaScript                       websites,” in Network and Distributed System Security Symposium
     ide services,” in 2013 35th International Conference on Software                        (NDSS 2022), 2022.
     Engineering (ICSE), 2013, pp. 752–761.
                                                                                        [43] H. Y. Kim, J. H. Kim, H. K. Oh, B. J. Lee, S. W. Mun, J. H. Shin,
[24] Field-based call graph construction for JavaScript. https://github.com/
                                                                                             and K. Kim, “DAPP: automatic detection and analysis of prototype
     Persper/js-callgraph.
                                                                                             pollution vulnerability in Node.js modules,” International Journal of
[25] A. Manion. Vulnerability disclosure policy. https://vuls.cert.org/                      Information Security, vol. 21, no. 1, pp. 1–23, 2022.
     confluence/display/Wiki/Vulnerability+Disclosure+Policy.
                                                                                        [44] P. Saxena, D. Akhawe, S. Hanna, F. Mao, S. McCamant, and D. Song,
[26] [MDN] public class fields. https://developer.mozilla.org/en-US/docs/                    “A symbolic execution framework for JavaScript,” in 2010 IEEE
     Web/{JavaScript}/Reference/Classes/Public class fields.                                 Symposium on Security and Privacy, 2010, pp. 513–528.
[27] ECMAScript 2023 language specification. https://tc39.es/ecma262/.
                                                                                        [45] B. Loring, D. Mitchell, and J. Kinder, “ExpoSE: Practical symbolic
[28] S. H. Jensen, P. A. Jonsson, and A. Møller, “Remedying the eval                         execution of standalone JavaScript,” in Proceedings of the 24th
     that men do,” in Proceedings of the 2012 International Symposium                        ACM SIGSOFT International SPIN Symposium on Model Checking
     on Software Testing and Analysis, ser. ISSTA 2012. New York,                            of Software, ser. SPIN 2017. New York, NY, USA: Association
     NY, USA: Association for Computing Machinery, 2012, p. 34–44.                           for Computing Machinery, 2017, p. 196–199. [Online]. Available:
     [Online]. Available: https://doi.org/10.1145/2338965.2336758                            https://doi.org/10.1145/3092282.3092295
[29] F. Meawad, G. Richards, F. Morandat, and J. Vitek, “Eval                           [46] J. F. Santos, P. Maksimović, T. Grohens, J. Dolby, and P. Gardner,
     begone! semi-automated removal of eval from JavaScript programs,”                       “Symbolic execution for JavaScript,” in Proceedings of the
     SIGPLAN Not., vol. 47, no. 10, p. 607–620, oct 2012. [Online].                          20th International Symposium on Principles and Practice of
     Available: https://doi.org/10.1145/2398857.2384660                                      Declarative Programming, ser. PPDP ’18. New York, NY, USA:
[30] K. Sen, S. Kalasapur, T. Brutch, and S. Gibbs, “Jalangi: A selective                    Association for Computing Machinery, 2018. [Online]. Available:
     record-replay and dynamic analysis framework for JavaScript,” in                        https://doi.org/10.1145/3236950.3236956
     Proceedings of the 2013 9th Joint Meeting on Foundations of
     Software Engineering, ser. ESEC/FSE 2013. New York, NY, USA:                       [47] W. Melicher, A. Das, M. Sharif, L. Bauer, and L. Jia, “Riding
     Association for Computing Machinery, 2013, p. 488–498. [Online].                        out DOMsday: Towards detecting and preventing DOM cross-site
     Available: https://doi.org/10.1145/2491411.2491447                                      scripting,” in Network and Distributed System Security Symposium
                                                                                             (NDSS), 2018, https://doi.org/10.14722/ndss.2018.23309.
[31] O. Arteau, “Prototype pollution attack in NodeJS application,” North-
     Sec, 2018.                                                                         [48] M. Steffens, C. Rossow, M. Johns, and B. Stock, “Don’t trust the
                                                                                             locals: Investigating the prevalence of persistent client-side cross-
[32] H. Y. Kim, J. H. Kim, H. K. Oh, B. J. Lee, S. W. Mun, J. H. Shin,
                                                                                             site scripting in the wild,” in Network and Distributed System Se-
     and K. Kim, “DAPP: automatic detection and analysis of prototype
                                                                                             curity Symposium (NDSS), 2019, https://publications.cispa.saarland/
     pollution vulnerability in Node.js modules,” International Journal of
                                                                                             id/eprint/2744.
     Information Security, pp. 1–23, 2021.
[33] Ocular interpreter. https://docs.shiftleft.io/ocular/interpreter.                  [49] G. Pellegrino, M. Johns, S. Koch, M. Backes, and C. Rossow,
                                                                                             “Deemon: Detecting csrf with dynamic analysis and property graphs,”
[34] N. Vasilakis, C.-A. Staicu, G. Ntousakis, K. Kallas, B. Karel, A. De-                   in Proceedings of the 2017 ACM SIGSAC Conference on Computer
     Hon, and M. Pradel, “Preventing dynamic library compromise on                           and Communications Security, ser. CCS ’17. New York, NY,
     Node.js via rwx-based privilege reduction,” in Proceedings of the                       USA: Association for Computing Machinery, 2017, p. 1757–1771.
     2021 ACM SIGSAC Conference on Computer and Communications                               [Online]. Available: https://doi.org/10.1145/3133956.3133959
     Security, 2021, pp. 1821–1838.
                                                                                        [50] X. Pan, Y. Cao, S. Liu, Y. Zhou, Y. Chen, and T. Zhou, “Cspautogen:
[35] R. Duan, O. Alrawi, R. P. Kasturi, R. Elder, B. Saltaformaggio, and
                                                                                             Black-box enforcement of content security policy upon real-world
     W. Lee, “Towards measuring supply chain attacks on package man-
                                                                                             websites,” in Proceedings of the 2016 ACM SIGSAC Conference on
     agers for interpreted languages,” arXiv preprint arXiv:2002.01139,
                                                                                             Computer and Communications Security, ser. CCS ’16, New York,
     2020.
                                                                                             NY, USA, 2016.
[36] C.-A. Staicu and M. Pradel, “Freezing the web: A study of ReDoS
     vulnerabilities in JavaScript-based web servers,” in 27th USENIX                   [51] Y. Cao, V. Yegneswaran, and Y. Chen, “Pathcutter: Severing the self-
     Security Symposium (USENIX Security 18), 2018, pp. 361–376.                             propagation path of xss JavaScript worms in social web networks.”
                                                                                             in NDSS, 2012.
[37] Z. Bai, K. Wang, H. Zhu, Y. Cao, and X. Jin, “Runtime recovery
     of web applications under zero-day redos attacks,” in 2021 IEEE                    [52] B. Eriksson, G. Pellegrino, and A. Sabelfeld, “Black widow: Blackbox
     Symposium on Security and Privacy (SP). IEEE, 2021, pp. 1575–                           data-driven web scanning,” in 2021 IEEE Symposium on Security and
     1588.                                                                                   Privacy (SP), 2021, pp. 1125–1142.



                                                                                   15
[53] M. Zhang and W. Meng, “Detecting and understanding JavaScript                  [67] X. Pan, Y. Cao, and Y. Chen, “I do not know what you visited
     global identifier conflicts on the web,” in Proceedings of the 28th                 last summer: Protecting users from third-party web tracking with
     ACM Joint Meeting on European Software Engineering Conference                       trackingfree browser,” in NDSS, 2015.
     and Symposium on the Foundations of Software Engineering,                      [68] M. Steffens, C. Rossow, M. Johns, and B. Stock, “Don’t trust the
     ser. ESEC/FSE 2020. New York, NY, USA: Association for                              locals: Investigating the prevalence of persistent client-side cross-site
     Computing Machinery, 2020, p. 38–49. [Online]. Available:                           scripting in the wild.” 2019.
     https://doi.org/10.1145/3368089.3409747
                                                                                    [69] S. Bensalim, D. Klein, T. Barber, and M. Johns, “Talking about my
[54] A. Fass, M. Backes, and B. Stock, “HideNoSeek: Camouflaging                         generation: Targeted dom-based xss exploit generation using dynamic
     malicious JavaScript in benign asts,” in Proceedings of the 2019                    data flow analysis,” in Proceedings of the 14th European Workshop
     ACM SIGSAC Conference on Computer and Communications                                on Systems Security, 2021, pp. 27–33.
     Security, ser. CCS ’19. New York, NY, USA: Association for
     Computing Machinery, 2019, p. 1899–1913. [Online]. Available:                  [70] I. Parameshwaran, E. Budianto, S. Shinde, H. Dang, A. Sadhu,
     https://doi.org/10.1145/3319535.3345656                                             and P. Saxena, “Dexterjs: Robust testing platform for dom-based
                                                                                         xss vulnerabilities,” in Proceedings of the 2015 10th Joint Meeting
[55] A. Fass, R. P. Krawczyk, M. Backes, and B. Stock, “JaSt: Fully                      on Foundations of Software Engineering, ser. ESEC/FSE 2015.
     syntactic detection of malicious (obfuscated) JavaScript,” in Detection             New York, NY, USA: Association for Computing Machinery, 2015,
     of Intrusions and Malware, and Vulnerability Assessment, C. Giuf-                   p. 946–949. [Online]. Available: https://doi.org/10.1145/2786805.
     frida, S. Bardin, and G. Blanc, Eds. Cham: Springer International                   2803191
     Publishing, 2018, pp. 303–325.
                                                                                    [71] S. Lekies, B. Stock, and M. Johns, “25 million flows later: large-
[56] Y. Cao, X. Pan, Y. Chen, and J. Zhuge, “Jshield: Towards real-time                  scale detection of dom-based xss,” in Proceedings of the 2013 ACM
     and vulnerability-based detection of polluted drive-by download                     SIGSAC conference on Computer & communications security, 2013,
     attacks,” in Proceedings of the 30th Annual Computer Security                       pp. 1193–1204.
     Applications Conference, ser. ACSAC ’14. New York, NY, USA:
     Association for Computing Machinery, 2014, p. 466–475. [Online].               [72] S. Lekies, K. Kotowicz, S. Groß, E. A. Vela Nava, and M. Johns,
     Available: https://doi.org/10.1145/2664243.2664256                                  “Code-reuse attacks for the web: Breaking cross-site scripting miti-
                                                                                         gations via script gadgets,” in Proceedings of the 2017 ACM SIGSAC
[57] Y. Cao, X. Pan, Y. Chen, J. Zhuge, X. Qian, and J. Fu, “Malicious
                                                                                         Conference on Computer and Communications Security, 2017, pp.
     code detection technologies,” Dec. 15 2015, US Patent 9,213,839.
                                                                                         1709–1723.
[58] M. Zhang and W. Meng, “Jsisolate: Lightweight in-browser
                                                                                    [73] C. Song, C. Zhang, T. Wang, W. Lee, and D. Melski, “Exploiting and
     JavaScript isolation,” in Proceedings of the 29th ACM Joint Meeting
                                                                                         protecting dynamic code generation.” in NDSS, 2015.
     on European Software Engineering Conference and Symposium on
     the Foundations of Software Engineering, ser. ESEC/FSE 2021.                   [74] X. Kang and S. Debray, “A framework for automatic exploit gen-
     New York, NY, USA: Association for Computing Machinery, 2021,                       eration for jit compilers,” in Proceedings of the 2021 Research on
     p. 193–204. [Online]. Available: https://doi.org/10.1145/3468264.                   offensive and defensive techniques in the Context of Man At The End
     3468577                                                                             (MATE) Attacks, 2021, pp. 11–19.
[59] Y. Cao, V. Rastogi, Z. Li, Y. Chen, and A. Moshchuk, “Redefining               [75] V. Kashyap, K. Dewey, E. A. Kuefner, J. Wagner, K. Gibbons,
     web browser principals with a configurable origin policy,” in 2013                  J. Sarracino, B. Wiedermann, and B. Hardekopf, “JSAI: A static
     43rd Annual IEEE/IFIP International Conference on Dependable                        analysis platform for JavaScript,” in Proceedings of the 22nd ACM
     Systems and Networks (DSN), 2013, pp. 1–12.                                         SIGSOFT International Symposium on Foundations of Software
                                                                                         Engineering, ser. FSE 2014. New York, NY, USA: Association
[60] Y. Cao, Y. Shoshitaishvili, K. Borgolte, C. Kruegel, G. Vigna, and
                                                                                         for Computing Machinery, 2014, p. 121–132. [Online]. Available:
     Y. Chen, “Protecting web-based single sign-on protocols against
                                                                                         https://doi.org/10.1145/2635868.2635904
     relying party impersonation attacks through a dedicated bi-directional
     authenticated secure channel,” in Research in Attacks, Intrusions and          [76] S. Bae, H. Cho, I. Lim, and S. Ryu, “Safewapi: Web api misuse
     Defenses, A. Stavrou, H. Bos, and G. Portokalidis, Eds. Cham:                       detector for web applications,” ser. FSE 2014. New York, NY,
     Springer International Publishing, 2014, pp. 276–298.                               USA: Association for Computing Machinery, 2014, p. 507–517.
                                                                                         [Online]. Available: https://doi.org/10.1145/2635868.2635916
[61] Y. Cao, Z. Li, V. Rastogi, and Y. Chen, “Virtual browser: A web-
     level sandbox to secure third-party JavaScript without sacrificing             [77] “Brave PageGraph,”        https://github.com/brave/brave-browser/wiki/
     functionality,” ser. CCS ’10. New York, NY, USA: Association                        PageGraph.
     for Computing Machinery, 2010, p. 654–656. [Online]. Available:                [78] U. Iqbal, P. Snyder, S. Zhu, B. Livshits, Z. Qian, and Z. Shafiq,
     https://doi.org/10.1145/1866307.1866387                                             “Adgraph: A graph-based approach to ad and tracker blocking,” in
[62] Y. Cao, Z. Chen, S. Li, and S. Wu, “Deterministic browser,”                         IEEE Symposium on Security and Privacy, May 2020.
     ser. CCS ’17. New York, NY, USA: Association for Computing                     [79] J. Park, J. Park, D. Youn, and S. Ryu, “Accelerating JavaScript
     Machinery, 2017, p. 163–178. [Online]. Available: https://doi.org/10.               static analysis via dynamic shortcuts,” in Proceedings of the 29th
     1145/3133956.3133996                                                                ACM Joint Meeting on European Software Engineering Conference
[63] S. Khodayari and G. Pellegrino, “JAW: Studying client-side CSRF                     and Symposium on the Foundations of Software Engineering,
     with hybrid property graphs and declarative traversals,” in 30th                    ser. ESEC/FSE 2021. New York, NY, USA: Association for
     USENIX Security Symposium (USENIX Security 21). USENIX                              Computing Machinery, 2021, p. 1129–1140. [Online]. Available:
     Association, Aug. 2021, pp. 2525–2542. [Online]. Available: https://                https://doi.org/10.1145/3468264.3468556
     www.usenix.org/conference/usenixsecurity21/presentation/khodayari              [80] G. Antal, Z. Tóth, P. Hegedűs, and R. Ferenc, “Enhanced bug predic-
[64] Y. Cao, S. Li, E. Wijmans et al., “(cross-) browser fingerprinting via              tion in JavaScript programs with hybrid call-graph based invocation
     os and hardware level features.” in NDSS, 2017.                                     metrics,” Technologies, vol. 9, no. 1, p. 3, 2020.
[65] S. Wu, P. Sun, Y. Zhao, and Y. Cao, “Him of many faces: Char-                  [81] G. Antal, P. Hegedus, Z. Tóth, R. Ferenc, and T. Gyimóthy, “Static
     acterizing billion-scale adversarial and benign browser fingerprints                JavaScript call graphs: A comparative study,” in 2018 IEEE 18th
     on commercial websites,” in 30th Annual Network and Distributed                     International Working Conference on Source Code Analysis and
     System Security Symposium, NDSS 2023, San Diego, California, USA,                   Manipulation (SCAM). IEEE, 2018, pp. 177–186.
     February 27 - March 3, 2023. The Internet Society, 2023.                       [82] T. R. Toma and M. S. Islam, “An efficient mechanism of generating
[66] S. Wu, S. Li, Y. Cao, and N. Wang, “Rendered private: Making glsl                   call graph for JavaScript using dynamic analysis in web application,”
     execution uniform to prevent webgl-based browser fingerprinting.” in                in 2014 International Conference on Informatics, Electronics & Vi-
     USENIX Security, 2019.                                                              sion (ICIEV). IEEE, 2014, pp. 1–6.



                                                                               16
[83] J. Dijkstra, “Evaluation of static JavaScript call graph algorithms,”         Algorithm 1 Bottom-up Abstract Interpretation
     Ph.D. dissertation, Software Analysis and Transformation, 2014.                1: procedure B OTTOM U P(stack← init, callDepGraph← init)
                                                                                    2:   while stack is not empty do
[84] D. Seifert, M. Wan, J. Hsu, and B. Yeh, “An asynchronous call graph            3:     func ←stack.POP() , addEdge←callDepGraph.addEdge
     for JavaScript,” in 2022 IEEE/ACM 44th International Conference                4:     Push func.scope.fns onto stack, update callDepGraph
     on Software Engineering: Software Engineering in Practice (ICSE-               5:     for stmt in func.stmts do
     SEIP). IEEE, 2022, pp. 29–30.                                                  6:       switch stmt do
                                                                                    7:          case resolvable-fn-call:
                                                                                                                      call
[85] M. Chakraborty, R. Olivares, M. Sridharan, and B. Hassanshahi,                 8:            addEdge(func−−→stmt.fn)
     “Automatic root cause quantification for missing edges in JavaScript           9:            stmt.fn.args.foreach(arg => resolve(
                                                                                                                  lookup
     call graphs,” in 36th European Conference on Object-Oriented Pro-             10:              arg.func−     −−− →*)
     gramming (ECOOP 2022). Schloss Dagstuhl-Leibniz-Zentrum für                  11:          case pending-fn-call:
     Informatik, 2022.                                                             12:            stmt.fn.lookupPath.args.foreach(arg =>
                                                                                                                              callback
                                                                                   13:              addEdge(arg.func−−−−−→stmt.fn))
                                                                                                                                     ret
[86] F. Yamaguchi, N. Golde, D. Arp, and K. Rieck, “Modeling and                   14:          case ret-fn-call: addEdge(func−−→stmt.fn)
     discovering vulnerabilities with code property graphs,” in 2014 IEEE          15:          case param-fn (callback):
     Symposium on Security and Privacy, 2014, pp. 590–604.                         16:            stack.push(stmt.fn)
                                                                                   17:            x ← isKnown?call:trigger
[87] M. Backes, K. Rieck, M. Skoruppa, B. Stock, and F. Yamaguchi,                 18:            target ← isSync?stmt.caller-fn:top
                                                                                                                          x
     “Efficient and flexible discovery of php application vulnerabilities,”        19:            addEdge(target−        →stmt.fn)
     in 2017 IEEE European Symposium on Security and Privacy (EuroS                20:          case return-fn:
                                                                                                                                                    ret
                                                                                   21:            stack.push(stmt.fn), resolve(stmt.fn−−→ ∗)
     P), 2017, pp. 334–349.
                                                                                   22:          case function expression: stack.push(stmt.fn)
[88] C. Pacheco and M. D. Ernst, “Randoop: Feedback-directed                       23:          case new Promise:
                                                                                   24:            stack.push(exec←stmt.executor)
     random testing for java,” in Companion to the 22nd ACM                                                           new                      call
                                                                                   25:            addEdge(func−−→Promise, func−−→exec)
     SIGPLAN Conference on Object-Oriented Programming Systems and                                                                     then
     Applications Companion, ser. OOPSLA ’07. New York, NY, USA:                   26:          case then: addEdge(stmt.prms−−→stmt.then)
                                                                                                                                         await
     Association for Computing Machinery, 2007, p. 815–816. [Online].              27:          case await: addEdge(stmt.prms−−−→stmt.await)
     Available: https://doi.org/10.1145/1297846.1297902                            28:          case resolve/reject:
                                                                                                                      resolve/reject
                                                                                   29:            addEdge(func−       −−−−−−−−    →stmt.defFunc.prms)
[89] M. Weiser, “Program slicing,” IEEE Transactions on software engi-             30:            resolve(stmt.defFunc.prms)
     neering, no. 4, pp. 352–357, 1984.                                            31:       end switch
                                                                                   32:     end for
[90] I. Mastroeni and D. Zanardini, “Abstract program slicing: An                  33:   end while
     abstract interpretation-based approach to program slicing,” ACM               34: end procedure
     Trans. Comput. Logic, vol. 18, no. 1, feb 2017. [Online]. Available:
     https://doi.org/10.1145/3029052                                               TABLE 8: A list of sources and sinks that are broken down
[91] H. S. Hong, I. Lee, and O. Sokolsky, “Abstract slicing: A new
                                                                                   by vulnerability types.
     approach to program slicing based on abstract interpretation and
                                                                                       Vulnerabilities                Sources                      Sinks
     model checking,” in Fifth IEEE International Workshop on Source
     Code Analysis and Manipulation (SCAM’05). IEEE, 2005, pp. 25–
                                                                                                            Arguments of functions in       functions in
     34.                                                                             Command Injection      module.exports,                 child_process
                                                                                                            command line arguments,
Appendix A.                                                                         Arbitrary Code Exec.
                                                                                                            environment variables,
                                                                                                            HTTP* requests                  eval, Function
Control-flow Graph Generation                                                                                                              file systems →
                                                                                       Path Traversal             HTTP* requests
                                                                                                                                           HTTP* responses
     Algorithm 1 shows a high-level overview of procedure
of generation of functional dependency and call edges. The                           *: “HTTP” includes HTTPS and third-party server packages such as Express.
algorithm accepts a stack holding all the functions to
analyze and an initially empty callDepGraph, which
                                                                                   Appendix B.
combines the known call graph (with resolved call edges)                           Source and Sink Discovery and Path Search
with the functional dependency graph. Specifically, FAST                                We describe how FAST discovers sources and sinks and
first pops up a function from the stack (Line 3), analyzes the                     then finds a control-flow path between sources and sinks.
function and pushes all function definitions in this function                      Note that a list of sources and sinks can be found in Table 8.
scope onto the stack for future analysis (Line 4). Then,                                Here are the details. First, FAST finds sources as
FAST loops through all statements in the function in the                           the start of a control flow path. There are generally
abstract domain (Line 5) and adds edges based on statement                         two source types: specific API calls and functions de-
types (Lines 6–29).                                                                fined in module.exports. The former can be found
     After bottom-up abstract interpretation, FAST searches                        via pattern matching; the latter needs a search on all
for interprocedural control-flow paths between sources and                         the defined functions. Specifically, FAST adopts a breadth
sinks of taint-style vulnerabilities. We remind readers that                       first search (BFS) to loop all possible objects start-
the FAST also constructs intra-procedural control flow                             ing from module.exports to all properties and sub-
graphs of each function but have omitted the description                           properties that are defined under module.exports. That
of this standard step for space reasons. The search has two                        is, FAST finds functions like module.exports.foo()
steps: locating sources and searching paths to sinks from                          and module.exports.foo().bar() as sources.
sources.                                                                                Second, FAST adopts a depth first search (DFS) to


                                                                              17
Algorithm 2 Extracting constraints from a data-flow path
                                                                                          TABLE 9: A breakdown of performance overhead of FAST
 1: map ← a map from object nodes to symbols                                              by different stages.
 2: procedure GET S YMBOLS(constraints, type, o0 , o1 , o2 , . . .)
 3:   for every oi in o0 , o1 , o2 , . . . do
 4:     if oi is in map then                                                                                strapi@4.0.8 strapi@3.0.0-beta.17.7 total.js@3.4.5
 5:        if map[oi ] has the same type as type then
                                                                                          Stage I: CF Path 1,298 ± 588        301 ± 41.8         1,534 ± 85.3
 6:           si ← map[oi ]
 7:        else                                                                           Stage II: DF Path 22.4 ± 1.59       3.20 ± 0.67         146 ± 110
 8:           try type conversion                                                         Stage III: Exploit 72.3 ± 14.0      41.7 ± 33.5         280 ± 342
 9:        end if
10:     else
11:        si ← map[oi ] ← MAKE S YMBOL(type)
                                                                                          TABLE 10: A list of CVE identifiers assigned to zero-day
12:        if oi has a concrete value then                                                vulnerabilities detected by FAST.
13:           constraints.ADD(MAKE C ONSTRAINT(=, si , oi .value))
14:        end if                                                                            CVE-2022-24431        CVE-2022-25855         CVE-2022-25908
15:     end if
                                                                                             CVE-2022-24377        CVE-2022-25923         CVE-2022-21191
16:   end for
17:   return s0 , s1 , s2 , . . .                                                            CVE-2022-25906        CVE-2022-25171         CVE-2022-25916
18: end procedure                                                                            CVE-2022-21129        CVE-2022-25853         CVE-2022-21810
19: procedure DF C ONSTR C ONV(constraints, sinkObj, conditions)                             CVE-2022-25962        CVE-2022-25890         CVE-2022-25350
20:   queue ← [sinkObj] + conditions                                                         CVE-2023-25805        CVE-2022-25926         CVE-2020-7735
21:   while queue is not empty do                                                            CVE-2020-7730         CVE-2020-15123         CVE-2020-15362
22:     head ← queue.POP()
23:     for each incoming edge e to head do
24:        e0 , e1 , e2 , . . . ← all edges in the same group with e
25:        o0 , o1 , o2 , . . . ← e0 .tail, e1 .tail, e2 .tail, . . .                     encounters constants, FAST adds a corresponding constant
26:        op ← operation of the edge group
27:        switch type of op do                                                           constraint.
28:           case string operations:
29:              s0 , s1 , s2 , . . . ← GET S YMBOLS(string, o0 , o1 , o2 , . . .)        Appendix D.
30:           case number operations:
31:              s0 , s1 , s2 , . . . ← GET S YMBOLS(number, o0 , o1 , o2 , . . .)        Performance Breakdown Evaluation
32:        end switch                                                                         We break down the performance overhead of three pack-
33:        constraints.ADD(MAKE C ONSTRAINT(=, head,
                 MAKE C ONSTRAINT(op, s0 , s1 , s2 , . . .)))                             ages with more than 10K LoC by three different stages.
34:     end for                                                                           Table 9 shows the breakdown. Stage I is the slowest because
35:   end while
36: end procedure                                                                         FAST needs to analyze all the function. Stage II is faster
                                                                                          than Stage I because FAST follows a subset of program with
                                                                                          control- and data-flow dependencies with the sink. Lastly,
                                                                                          Stage III is also relatively slow (much faster than Stage I but
find a control-flow path from sources to sinks. The search                                slower than Stage II), because its takes time for Z3 solver
follows the timing sequence of call edges on a specific                                   to find a solution given constraints.
statement. For example, say we have func1(func2())
or func2().func1(). In both cases, FAST searches                                          Appendix E.
through func2() first and then reaches func1() to en-                                     A List of CVE Identifiers for Zero-day Vulner-
sure the feasibility of the following data-flow path gener-
ation stage. FAST also limits the number of times that                                    abilities
a statement can be visited to avoid loops in the control-                                     Table 10 lists 21 CVE identifiers that are assigned to
flow path. Note that this is unrelated with the follow-up                                 zero-day vulnerabilities found by FAST.
top-down abstract interpretation, which can still explore
functions recursively.

Appendix C.
Constraint Generation
    Algorithm 2 shows a simplified algorithm of constraint
generation. Given an object, FAST loops through all the
incoming edges to the object (Line 23), obtain objects
related to incoming edges (Line 25) and the operator (Line
26). Then, FAST obtains symbols for this operator based
on the type (Line 27) and then adds the constraint to the
pool (Line 33). The symbol generation and lookup process
is shown in Lines 2–18. FAST maintains a map between
symbols (which are acceptable by constraint solvers) and
object nodes (Line 1). When FAST accepts an operator and
their operands, FAST tries to lookup or generate symbols
(Line 11). Note that if there are type issues, FAST will
attempt to perform type conversion (Line 8) and if FAST


                                                                                     18
